Google Safe Browsing Lookup API를 사용해 모든 외부 링크의 보안 상태 표시

며칠 전, 포럼 회원들이 제공한 외부 링크로부터 내 웹사이트를 보호하는 전략을 찾고 있었습니다. 유해 URL에 대한 자체 데이터베이스를 구축해 보려 했지만, 생각보다 훨씬 어려웠습니다. 링크의 양이 워낙 방대해서 분류하기가 쉽지 않았죠. 그래서 제 요구사항에 맞는 API 서비스를 찾아보다가 구글이 제공하는 무료 서비스인 Google Safe Browsing API를 발견했고, 이를 Discourse 포럼에 추가해 보기로 했습니다.

제 포럼에서의 결과
*마우스를 올렸을 때

다음과 같이 진행했습니다.
Discourse에서 모든 외부 링크 뒤에 아이콘을 추가하고, Google Safe Browsing Lookup API(v4)를 사용하여 링크의 보안 상태를 표시하는 툴팁을 보이려면 다음 단계를 따르세요:

  1. Google Safe Browsing용 API 키 생성:

    • Google Cloud Console로 이동하세요.
    • 새 프로젝트를 생성하거나 기존 프로젝트를 사용하세요.
    • Safe Browsing API를 활성화하세요.
    • 해당 프로젝트에 대한 API 키를 생성하세요.
      *모든 것이 무료입니다.
  2. Discourse 관리자 로그인:

    • 관리자 계정으로 Discourse에 로그인하세요.
  3. 테마 사용자 정의 접근:

    • “관리자” → “사용자 정의” → "테마"로 이동하세요.
    • 편집할 테마를 선택하거나 새 테마 또는 테마 구성 요소를 생성하세요.
  4. CSS 추가:

    • 원하는 테마의 "CSS/HTML 편집"을 클릭하세요.

    • “CSS” 탭에 다음 코드를 추가하세요:

      a[target="_blank"]:after {
          content: url('https://example.com/icon.png'); /* 원하는 아이콘의 URL로 교체하세요 */
          margin-left: 5px; /* 링크와 아이콘 사이의 간격 */
          display: inline-block;
      }
      
      .tooltip {
          position: relative;
          display: inline-block;
      }
      
      .tooltip .tooltiptext {
          visibility: hidden;
          width: 200px;
          background-color: #555;
          color: #fff;
          text-align: center;
          border-radius: 6px;
          padding: 5px;
          position: absolute;
          z-index: 1;
          bottom: 125%; /* 툴팁 위치 */
          left: 50%;
          margin-left: -100px;
          opacity: 0;
          transition: opacity 0.3s;
      }
      
      .tooltip:hover .tooltiptext {
          visibility: visible;
          opacity: 1;
      }
      
  5. JavaScript 추가:

    • 테마의 “Header” 탭에 다음 코드를 추가하세요:

      <script type="text/discourse-plugin" version="0.8">
        api.onPageChange(() => {
          const externalLinks = document.querySelectorAll('a[href^="http"]:not([href*="' + window.location.hostname + '"])');
          const apiKey = 'YOUR_GOOGLE_API_KEY'; // API 키로 교체하세요
          const lookupUrl = 'https://safebrowsing.googleapis.com/v4/threatMatches:find?key=' + apiKey;
      
          externalLinks.forEach(link => {
            link.setAttribute('target', '_blank');
            link.classList.add('tooltip');
      
            const tooltipText = document.createElement('span');
            tooltipText.className = 'tooltiptext';
            tooltipText.innerText = '링크 안전성 확인 중...';
            link.appendChild(tooltipText);
      
            fetch(lookupUrl, {
              method: 'POST',
              headers: {
                'Content-Type': 'application/json'
              },
              body: JSON.stringify({
                client: {
                  clientId: 'yourcompany',
                  clientVersion: '1.5.2'
                },
                threatInfo: {
                  threatTypes: ['MALWARE', 'SOCIAL_ENGINEERING'],
                  platformTypes: ['ANY_PLATFORM'],
                  threatEntryTypes: ['URL'],
                  threatEntries: [
                    { url: link.href }
                  ]
                }
              })
            })
            .then(response => response.json())
            .then(data => {
              if (data.matches && data.matches.length > 0) {
                tooltipText.innerText = '경고: 이 링크가 안전하지 않을 수 있습니다!';
                tooltipText.style.backgroundColor = '#ff0000';
              } else {
                tooltipText.innerText = '이 링크는 안전합니다.';
              }
            })
            .catch(error => {
              tooltipText.innerText = '링크 안전성 확인 중 오류가 발생했습니다.';
            });
          });
        });
      </script>
      

      이 JavaScript는 다음을 수행합니다:

      • 모든 외부 링크를 찾아 새 탭에서 열리도록 설정합니다.
      • 링크 뒤에 아이콘을 추가합니다.
      • Google Safe Browsing API를 사용하여 링크를 확인하고 결과를 툴팁에 표시합니다.
  6. 테마 저장 및 적용:

    • 변경 사항을 저장하려면 "저장"을 클릭하세요.
    • 새 테마를 생성한 경우 기본 테마로 설정하거나 원하는 사용자 그룹에 할당하세요.

이 단계를 따르면 Discourse 인스턴스의 모든 외부 링크 뒤에 아이콘이 표시되고, Google Safe Browsing API를 사용하여 링크의 보안 상태를 보여주는 툴팁이 나타납니다.

보안을 위한 수정 사항
Google API 사용에 대한 웹사이트 제한(Website limits) 섹션에 웹사이트를 추가해야 합니다.

옵션

Font Awesome 아이콘 사용

Font Awesome 아이콘을 사용하려면 아래는 업데이트된 CSS입니다:

Font Awesome 아이콘이 포함된 업데이트된 CSS

/* Font Awesome이 포함되었는지 확인 */
@import url('https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css');

/* 외부 링크 뒤에 Font Awesome 아이콘 추가 */
a[target="_blank"]:after {
    content: "\f35d"; /* Font Awesome 외부 링크 아이콘의 유니코드 */
    font-family: 'Font Awesome 5 Free'; /* Font Awesome 패밀리 */
    font-weight: 900; /* Font Awesome 솔리드 가중치 */
    margin-left: 5px; /* 링크와 아이콘 사이의 간격 */
    display: inline-block;
}

설명:

  1. Font Awesome 가져오기:

    • CSS는 CDN에서 Font Awesome CSS를 가져오는 것으로 시작합니다.
  2. 아이콘 추가:

    • a[target="_blank"]:after 선택자의 content 속성은 Font Awesome 외부 링크 아이콘의 유니코드인 "\f35d"로 설정됩니다.
    • font-family는 'Font Awesome 5 Free’로, font-weight는 아이콘의 솔리드 버전을 사용하기 위해 900으로 설정됩니다.

이 글이 저와 같은 것을 찾고 있는 분들에게 도움이 되기를 바랍니다.

정말 멋진 아이디어네요!!

:warning: 테마 컴포넌트에 Google Cloud API 키를 포함하면 해당 키가 외부에 노출됩니다. 타인이 API 키에 접근할 경우, 매우 큰 비용 청구서가 나올 수 있습니다. :warning:

API 키의 범위를 적절히 제한하는 것(예: Safe Browsing API에만 접근 가능하도록 설정) 또는 서버 측 프록시를 추가하여 API 키를 서버에서 안전하게 관리하는 방법을 권장합니다.

아니면 … 이걸 플러그인으로 만들어서 아무도 볼 수 없는 배경에서 API 통신을 계속 진행하세요.

제가 말하려던 게 바로 그거였어요 :wink:

프록시는 일종의 특별한 중개자이기도 하며, 좋은 아이디어이기도 합니다.

확실히 OR이었습니다.

훌륭한 조언 감사합니다. :blush: Google Cloud Console에서 API 접근을 차단하는 메커니즘이 있는 것 같습니다. 해당 웹사이트를那里에 추가하세요.

플러그인을 작성할 수 있는 기술이 부족해서요. 제 생각에 이 전략은 기본적이고 직관적인 방법입니다. 하지만 플러그인을 만들려는 분들이 있다면, 제 방식보다 훨씬 낫다고 생각합니다. 그리고 그렇게 된다면 정말 기쁠 것 같아요.

그리고 그 사이에, 이 건이 기능 요청으로 가치가 있다고 제안하고 싶습니다.

(해당 서비스가 책임이 있는지는 제가 알지 못합니다)

무엇을 약속하진 못하지만, 앞으로 몇 달 안에 시간이 나면 이 제안을 반드시 검토해 보겠습니다.

관심을 가져주셔서 정말 기쁩니다. 그리고 이 플러그인을 만들겠다는 의사를 보여준 점도 감사드립니다.

이것이 해당 기능에 포함되기를 바라며, 그렇게 되어 기쁩니다.

이 기능이 Discourse의 셀프 호스팅 버전에서도 작동하는지 아시는 분 계신가요? 관리자 화면에서 테마 설정을 "사용자 정의"할 수 있는 옵션이 보이지 않습니다.

해당 테마에는 테마 설정이 없으며, 설정 파일이 비어 있습니다:

또한, GitHub에서 다운로드한 테마를 변경하려면 포크를 생성한 후 해당 포크를 수정하고, 그 포크를 사용해야 합니다.

새로운 테마 컴포넌트를 생성하여 테마에 추가할 수 있습니다.