`<a>` target=_"blank"와 rel="noreferrer noopener"를 함께 사용해야 할까?

작성 날짜

HTML <a> 요소(앵커 요소)는 href 특성을 통해 다른 페이지나 같은 페이지의 어느 위치, 파일, 이메일 주소와 그 외 다른 URL로 연결할 수 있는 하이퍼링크를 만듭니다. <a> 안의 콘텐츠는 링크 목적지의 설명을 나타내야 합니다.

<ul>
  <li><a href="https://example.com">Website</a></li>
  <li><a href="mailto:m.bluth@example.com">Email</a></li>
  <li><a href="tel:+123456789">Phone</a></li>
</ul>
  • href
  • rel
  • target

target

  • _self: URL을 현재 브라우징 맥락에 표시합니다. 기본값.
  • _blank: URL을 새로운 브라우징 맥락에 표시합니다. 보통 새 탭이지만, 사용자가 브라우저 설정을 통해 새 창으로 바꿀 수 있습니다.=
  • _parent: URL을 현재 브라우징 맥락의 부모에 표시합니다. 부모가 존재하지 않으면 _self와 동일하게 행동합니다.
  • _top: URL을 최상단 브라우징 맥락(현재 맥락의 부모면서 자신의 부모가 존재하지 않는, 제일 높은 맥락)에 표시합니다. 부모가 존재하지 않으면 _self와 동일하게 행동합니다.

보안상 취약점

  • 링크된 페이지의 JavaScript에서 window.opener로 부모 윈도우(링크가 걸려있는 페이지)의 오브젝트에 접근해서 window.opener.location=새로운 URL로 부모 윈도우의 URL을 바꿔칠 수도 있다.
  • 이렇게 부모 윈도우의 정보를 사용하거나 조작하여 개인정보를 유출시키는 가짜 페이지로 부적절한 리다이렉션을 하는 등 보안상 심각한 문제가 발생할 수 있다.

Reverse tabnabbing

Reverse tabnabbing은 새로운 탭이나 창에서 열린 외부 링크가 원래 페이지를 조작할 수 있는 취약점을 의미합니다. 이 공격은 window.opener 객체를 이용해 이루어집니다.

  1. 사용자가 웹 페이지 A를 방문합니다.
  2. 웹 페이지 A에서 외부 링크를 클릭해 새 탭/창에서 페이지 B로 이동합니다.
  3. 악의적인 페이지 B가 window.opener 객체를 이용해 페이지 A에 접근하고 조작합니다. 예를 들어, 페이지 A를 피싱 사이트로 리다이렉트할 수 있습니다.

이렇게 하면 사용자는 새로운 탭을 닫기 전까지는 원래 페이지가 조작되었다는 것을 눈치채지 못할 수 있습니다.

어떻게 해결하는가?

rel="noreferrer noopener" 를 추가하여 링크된 페이지를 열때 referrer(이전 페이지)와 opener(새 페이지를 연 사람) 정보가 생기는데 두 정보를 아예 없앴다.

<a href="https://example.com" target="_blank" rel="noreferrer noopener">
  Website
</a>
  • rel 속성: 링크된 페이지와의 관계(relationship). 공백으로 구분해서 관계 목록을 나열하여 지정할 수 있다.
  • noopener: 링크된 페이지에서 window.opener로 부모 윈도우를 참조할 수 없게 된다. 그리고 링크된 페이지와 부모 윈도우는 별개의 프로세스로 취급되어서 서로 퍼포먼스에 영향을 미치지 않는다.
  • noreferrer: 다른 페이지로 이동 시 링크를 건 페이지의 정보를 브라우저가 Referer HTTP 헤더로 송신하지 않는다.

백 링크가 있는 경우

'noopener noreferrer' 지시문이 사용되지 않았을 때 부모 페이지와 자식 페이지 간의 링크:

1.png

백 링크가 없는 경우

'noopener noreferrer' 속성이 사용되었을 때 부모 페이지와 자식 페이지 간의 링크:

2.png

접근 가능한 속성

악의적인 사이트는 Cross Origin(크로스 도메인) 접근의 경우, opener 자바스크립트 객체 참조(실제로는 window 자바스크립트 클래스 인스턴스에 대한 참조)로부터 다음 속성만 접근할 수 있습니다:

  • opener.closed: 창이 닫혔는지 여부를 나타내는 불리언 값 반환.
  • opener.frames: 현재 창에 있는 모든 iframe 요소를 반환.
  • opener.length: 현재 창에 있는 iframe 요소의 수를 반환.
  • opener.opener: 창을 생성한 창에 대한 참조를 반환.
  • opener.parent: 현재 창의 부모 창을 반환.
  • opener.self: 현재 창을 반환.
  • opener.top: 가장 상위 브라우저 창을 반환.
  • 도메인이 같다면 악의적인 사이트는 window 자바스크립트 객체 참조에 의해 노출된 모든 속성에 접근할 수 있습니다.
  • 최근의 브라우저(Firefox 79+ 등)에서는 target="_blank"를 지정하면 rel="noopener"를 적용한 것과 같은 동작을 합니다.
  • 결론: noreferrer 만 사용해도 문제 없다

접근성 떨어지는 약한 링크 텍스트

심각하게 흔한 실수는 "여기를 클릭"이나 "여기"라는 단어에 링크를 한다는 것입니다.

<p>저희의 제품을 더 알아보시려면 <a href="/products">여기</a>를 클릭하세요.</p>

강한 링크 텍스트

다행히도 쉽게 수정할 수 있는 데다가, 접근성이 떨어지는 버전보다 더 짧습니다!

<p>저희의 <a href="/products">제품을 더 알아보세요</a>.</p>

하이퍼링크는 진짜 URL로의 탐색 용도로만 사용

href를 # or javascript:void(0)로 지정해 페이지 새로고침을 막거나

이런 가짜 href 값은 링크를 복사하거나 드래그할 때, 링크를 새 탭이나 새 창에서 열 때, 즐겨찾기에 추가할 때와 JavaScript를 불러오고 있거나 스크립트 오류가 발생했을 때, 아니면 비활성화했을 때 예측하지 못한 동작을 유발합니다. 또한 스크린 리더 등 보조 기술에도 잘못된 의미를 전달합니다.

외부 링크와 비 HTML 리소스 링크

target="_blank"로 인해 새 탭/창을 여는 링크와, 파일을 다운로드하는 링크는, 링크를 클릭했을 때 무슨 일이 발생할건지 명시해야 합니다.

시력이 나쁘거나 스크린 리더로 탐색하는 사용자, 혹은 지각 능력이 낮은 사용자는 예상하지 못한 상황에서 새 탭, 새 창, 다른 앱이 켜지는 순간 혼란스러울 수 있습니다. 오래된 스크린 리더는 이런 상황을 아예 알려주지 못할 수도 있습니다.