배경
🍷
개발 •  • 읽는데 7분 소요

하이퍼링크를 신뢰할 수 없다면? noopener, noreferrer, nofollow

HTML 앵커 태그의 rel 속성으로 해당 값을 적용해야 하는 이유에 대해 알아봅니다.

#Front-End


개노답 하이퍼링크를 지켜주는 수호신 삼형제…?

여러분이 자주 방문하는 웹사이트에 접속하여 개발자 도구를 한 번 켜보세요. 그리고 HTML 요소 검사기에서 하이퍼링크(<a>, 앵커 태그)들을 살펴보세요.

노션 왼쪽은 노션, 오른쪽은 디스커스(Disqus). 일부 웹앱의 하이퍼링크에는 rel 속성의 값으로 noopener, noreferrer, nofollow 가 걸려있는 걸 볼 수 있다

아마 전부는 아니지만, 일부 하이퍼링크에는 rel 이라는 속성의 값으로 noopener, noreferrer, 또는 nofollow 가 들어있을 겁니다. 이러한 속성은 왜 들어있으며 어떤 역할을 하는 걸까요? 오늘은 그 이유에 대해 간단히 알아보고자 합니다.

이번 포스트를 통해 하이퍼링크의 rel 속성으로 해당 값을 언제 어떻게 적용해야 하는지가 궁금하신 분 들께 도움이 되기를 바랍니다.

TL;DR

  • noopener 는 하이퍼링크에 target="_blank" 속성이 적용된 링크를 열 때 발생할 수 있는 탭 내빙(Tab nabbing) 공격을 방지하기 위해 사용된다.
  • noreferrernoopener 의 기본 동작에 더해, 해당 HTML 파일을 불러오기 위한 HTTP 요청을 보낼 때 Referer 헤더를 생략한다. 따라서 해당 사이트의 통계 수집에 영향을 줄 수 있다.
  • nofollow 는 하이퍼링크로 연결된 페이지를 신뢰할 수 없기 때문에 검색 엔진이 현재 웹사이트와 링크된 페이지를 연결하지 않기를 바라거나, 링크된 페이지를 크롤링하지 않기를 바라는 경우에 사용한다.
  • 위 속성들은 신뢰할 수 없는 불특정다수가 웹사이트에 링크를 올릴 수 있는 기능이 제공될 때 적용하는 것이 적합하다.

noopener

여러분이 하이퍼링크를 클릭할 때, 해당 앵커 태그에 target="_blank" 속성이 적용되어 있었다면 두 가지 재미있는 일이 발생합니다. 바로 성능과 보안 면에서 취약점이 발생한다는 것입니다.

우선 새롭게 열린 페이지는 원본 페이지와 동일한 프로세스에서 실행될 수 있습니다. 이는 새로 열린 페이지가 보조 브라우징 컨텍스트(auxiliary browsing context) 이기 때문에, 자기 자신을 생성한 원본 브라우징 컨텍스트를 오프너 브라우징 컨텍스트(opener browsing context) 라는 이름으로 참조하고 있기 때문입니다.

따라서 새 페이지에서 많은 JavaScript를 실행하는 경우에는 원본 페이지의 성능이 저하될 수도 있습니다(아래에 설명할 내용이지만 최신 브라우저에서는 이러한 성능 문제가 기본적으로 발생하지 않게 처리가 된 상태입니다).

하지만 가장 큰 문제는 새롭게 열린 페이지에서 JavaScript를 통해 원본 페이지에 직접 접근이 가능해진다는 것입니다. 이 원본 페이지는 window.opener 를 이용해 접근이 가능한데, 이를 이용한 피싱 공격을 탭 내빙(Tab nabbing) 이라는 이름으로 부릅니다.

1 탭 내빙의 순서도

탭 내빙을 간단하게 설명해 둔 자료가 있어 이를 가져왔습니다.

  1. 우선 사용자가 cgm.example.com 에 접속하고, 해당 사이트에서 happy.example.com 으로 갈 수 있는 외부 링크를 클릭합니다.
  2. 새 탭에서 happy.example.com 가 열립니다. 이 때 happy.example.com 에서 window.opener.location 을 피싱 사이트인 cgn.example.com/login 으로 변경합니다.
  3. 사용자가 본래의 탭으로 돌아오면, 로그인이 풀렸다고 생각하고 아이디와 비밀번호를 입력합니다.
  4. 피싱 사이트는 아이디와 비밀번호를 탈취한 후 다시 cgm.example.com 으로 리다이렉션하므로 사용자는 눈치챌 수 없습니다.

이러한 탭 내빙 공격은 불특정다수가 외부 링크를 다른 사람에게 공유할 수 있는 메일이나 인터넷 커뮤니티 등에서 많이 발생했습니다. 여기에 대한 근본적인 원인은 바로 window.opener 를 이용해 원본 페이지에 접근이 가능했기 때문이었죠.

noopener 속성값은 이를 막기 위해 등장했습니다. noopener 를 적용하면 원본 페이지에 대한 컨텍스트 액세스를 제공하지 않고 새 탭에서 최상위 브라우징 컨텍스트를 새로 생성하여 링크를 열도록 브라우저에게 지시합니다. 따라서 새 페이지에서의 window.opener 의 값은 null 이 됩니다. 단순히 브라우징 컨텍스트의 연결을 끊어달라고 브라우저에게 말하는 것이기 때문에, 검색 엔진 최적화(SEO)에는 영향을 미치지 않습니다.

만약 이 글을 읽은 여러분께서 나 여태껏 저렇게 코드 작성하지 않았는데 어떡하죠? 라는 걱정이 드실 수도 있습니다. 다행스럽게도 이러한 문제점은 웬만해서 발생하지 않을 것입니다.

우선 이러한 탭 내빙 문제는 target="_blank" 일 때만 발생하고, 댓글처럼 하이퍼링크의 소스를 신뢰할 수 없는 사용자로부터 받아야 할 경우에만 적용하면 됩니다. 여러분이 직접 믿음직스러운(?) 사이트의 링크를 걸었다면 그것은 문제될 일이 없습니다.

또한 HTML 표준의 변경에 맞추어, 크롬의 경우 88버전, 사파리의 경우 68버전 부터 target="_blank" 가 달린 하이퍼링크의 기본 동작이 noopener 로 변경되었습니다.

noopener HTML 스펙을 보면 noopenernoreferrer가 없어도 기본적으로 noopener 취급을 한다고 되어있다

그런데 연도로 따지자면 사파리는 2018년, 크롬은 2021년에 기본 동작이 변경되었으므로 생각보다 최근에 적용된 것이었네요. noopener 가 기본 동작이 된 이상 앞으로 점차 사라지게 될 속성이지 않을까 생각해 봅니다.

만약 굳이 새 탭에서 window.opener 에 대한 접근이 필요하다면, rel="opener" 를 설정하는 방식으로 해결 가능합니다.

noreferrer

헤더 ㄴㄴ HTML 스펙을 보면 noreferrer의 경우에는 특정 헤더를 생략하라고 되어 있다

noopener의 기능과 동일하게, noreferrer는 새로 열린 사이트가 window.opener 객체에 접근하는 것을 방지하는 역할을 합니다.

하지만 여기서 noreferrer 에는 더 나아가 추가 기능이 포함되어 있는데, 바로 브라우저가 해당 페이지를 불러오면서 HTTP 요청을 보낼 때 referer 헤더를 생략하는 것입니다. 즉, noreferrer 설정값이 있으면 링크를 클릭할 때 해당 유입이 어디에서 발생되었는지에 대한 정보가 새 페이지에 제공되지 않습니다.

가령 누군가가 자신의 웹페이지에 여러분의 링크를 noreferrer 를 사용해 하이퍼링크를 건 다음, 사용자가 해당 링크를 클릭하면 해당 사용자가 어디에서 왔는지를 알 수 없습니다. 따라서 구글 애널리틱스와 같은 통계 분석 도구에서 해당 트래픽은 추천 유입(Referral)이 아닌 직접 유입(Direct) 으로 나타납니다. 따라서 해당 웹페이지의 통계 수치를 어느 정도 왜곡할 수 있다(?)는 특징이 있겠네요. SEO에는 영향을 미치지 않습니다. 물론 마케팅 담당하시는 분께는 이런 것들이 캠페인 목표 설정하는 데 있어 조금 문제가 될 수도…

그렇다면 언제 noreferrer 를 사용해야 하고 언제 noopener 를 사용해야 할까요? 일반적으로는 둘 다 사용하는 것이 좋습니다. 이는 브라우저 호환성 때문이고, 문제를 일으키는 녀석은… 역시 IE입니니다. 대부분의 최신 브라우저는 두 속성 모두를 지원하지만 일부 구형 브라우저의 경우에는 noreferrer 만 지원하는 경우가 있기 때문입니다. 따라서 일반적으로는 두 속성값을 모두 적어주어야 합니다.

노래퍼 위는 noopener, 아래는 noreferrer. 실제로 개발자 도구의 네트워크 탭에서 HTTP 헤더 차이를 볼 수 있다.

개발자 도구에서 볼 때는 HTTP 헤더 이름이 r이 하나 빠진 referer라고 되어 있는데 이건 놀랍게도(?) HTTP 사양을 정의할 때 오타낸 것이 그대로 표준이 된 경우(…)라고 합니다.

nofollow

노팔로 nofollow는 일반적으로 댓글에 달리는 링크에 주로 사용한다

nofollow 속성값은 검색 엔진에게 링크된 웹사이트를 보증하거나 신뢰할 수 없으니 현재 웹사이트와 연결하지 않기를 바라는 경우에 사용합니다.

사실 nofollow 속성값이 등장한 이유가 바로 스팸 댓글 때문입니다. 구글은 웹사이트 간 링크 연결 그래프, 페이지의 평판과 신뢰도를 바탕으로 검색 결과를 순위화하는 알고리즘인 PageRank를 개발했는데, 각 블로그에 무분별하게 달린 스팸 댓글이 페이지 순위에 영향을 주었죠.

스팸 댓글이 달린 게시글은 사이트 소유자의 의도와는 상관없이 평판이 내려가게 되었는데요, 구글은 이에 대한 해결책으로 nofollow 옵션이 제공된 링크는 크롤링하지 않고 검색 엔진에도 영향을 미치지 않게 로직을 바꾸기로 했습니다. 따라서 일반적으로 nofollow 속성값은 댓글이나 포럼처럼 사용자가 참여 콘텐츠의 링크에 적합합니다. 위의 두 속성에 비교하자면 실제로 웹사이트 SEO에 영향을 미칠 수도 있죠.

사실 구글에서는 이처럼 사이트 소유자가 완전히 신뢰할 수는 없지만, PageRank의 순위에는 영향을 미칠 수 있는 링크의 종류를 추가로 구분해두었습니다. 왜냐하면 nofollow 속성값을 이용해 돈이 되는 링크들만 검색 엔진에 전달하는 어뷰징 사례가 있었거든요.

그래서 돈을 받고 광고를 걸어주는 링크 같은 경우는 sponsored, 댓글 및 포럼과 같은 사용자 참여 콘텐츠는 ugc 등으로 구분합니다. 하지만 개인적 경험 상으로 이정도 수준까지 엄격하게 속성을 구분해둔 적은 없었던 것 같아서, 일단은 이런 게 있다 정도만 알아두면 좋을 것 같습니다. (위의 예시 이미지에서는 ugc 를 함께 명시해준 모습을 볼 수 있네요.)

하지만 nofollow 속성값이 적용되었다고 해서 해당 페이지의 크롤링을 완전히 막을 수는 없습니다. 해당 페이지가 다른 웹사이트의 링크로부터 연결될 수가 있기 때문이죠. 따라서 본인 소유의 페이지 크롤링을 막기 위해선 nofollow 속성값을 쓰기보다는 robots.txt 또는 메타 태그를 이용하는 것이 가장 좋은 방법입니다.

참고 자료

이 포스트가 유익하셨다면?




프로필 사진

👨‍💻 정종윤

글 쓰는 것을 좋아하는 프론트엔드 개발자입니다. 온라인에서는 재그지그라는 닉네임으로 활동하고 있습니다.


Copyright © 2024, All right reserved.

Built with Gatsby