`<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
객체를 이용해 이루어집니다.
- 사용자가 웹 페이지 A를 방문합니다.
- 웹 페이지 A에서 외부 링크를 클릭해 새 탭/창에서 페이지 B로 이동합니다.
- 악의적인 페이지 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' 지시문이 사용되지 않았을 때 부모 페이지와 자식 페이지 간의 링크:
백 링크가 없는 경우
'noopener noreferrer' 속성이 사용되었을 때 부모 페이지와 자식 페이지 간의 링크:
접근 가능한 속성
악의적인 사이트는 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"
로 인해 새 탭/창을 여는 링크와, 파일을 다운로드하는 링크는, 링크를 클릭했을 때 무슨 일이 발생할건지 명시해야 합니다.
시력이 나쁘거나 스크린 리더로 탐색하는 사용자, 혹은 지각 능력이 낮은 사용자는 예상하지 못한 상황에서 새 탭, 새 창, 다른 앱이 켜지는 순간 혼란스러울 수 있습니다. 오래된 스크린 리더는 이런 상황을 아예 알려주지 못할 수도 있습니다.