home

noopenner, noreferrer의 필요성

글 분류
main
키워드
보안
html
생성일
2023/02/04 08:40
최근 수정일
2025/02/20 07:17
작성중

⚽️ 목표

다른 페이지를 새창에 띄울 시 "noopener를 꼭해줘야 해요! 보안적 허점이 생긴다구요오오옷~!" 라고 뭉뚱그리지 말고 왜 취약한지 araboza

TL;DR

최신 브라우저에서는 noopener attribute가 없어도 새로열린 자식창에서 부모창 참조는 불가능하다.
최신 브라우저에는 noopener를 명시적으로 적어주지 않아도 target="\_blank"시 기본적으로 적용되어있다.
“그렇다고... 당신이 만든 웹의 이용자가 모두 최신 브라우저를 사용한다는 착각에 빠.져.있.진.안.켔.찌???!?!”
생명체의 절반이 사라져야 합니다

1. 들어가기 전에

새로운 페이지를 새 창에서 열 시 noopener attribute가 없으면 자식창에서 window.opener 객체를 이용해 부모창을 제어할 수 있다.
noopener를 사용해야하는 이유는 그러하다. → 적어도 우리가 알고있기에는.
다들 “취약하니까 넣어야한다.”를 이야기하고 있으니 우리는 모두 해당 attribute를 넣어야한다는 사실을 알고 있다.
이 글은 “어떤 취약점인가?”, “왜 취약한가?”, “아직도 취약한가?”, “최신 브라우저는 업데이트 안하고 뭐하나?”와 같은 후속질문에 대답하기 위한 글이다.
물론 “취약점이니까 a태그에서 새로운 창을 열때는 무조건 noopener attribute를 넣어야한다.”에서 그냥 무조건 넣기만해도 일반적인 업무나 협업 관점에서는 충분하지만 본인의 생각은 조금 다르다.
내가 어떠한 액션을 취할때는 그에 합당한 이유와 최소한의 히스토리를 알아야 문제가 생겼을때 대응이 가능하기 때문이다. 이 두개가 충족되지 않을 경우 문제가 생겼을 때 해당 문제는 예기치 못한 문제가 발생하는 경우가 많았다.

2. 1 회차 시도

왜 1차 시도인지는 읽다보면…

유도하는 상황

graph TD
 A(A - 취약한 링크를 가지고 있는 페이지) ---> B(B - A로인해 열린 페이지)
 B-. 부모페이지 참조 시도 .-> A
Mermaid
복사
1.
A페이지에 취약한 링크를 삽입하여 이용자가 클릭하도록 유도한다.
2.
A로부터 열린 B 페이지에서 window.opener를 참조 시도한다.
결과적으로 2번 단계에서는 B가 window.opener.location를 이용하여 부모 페이지인 A의 현재 주소를 변조하여 피싱사이트로 이용자를 이동시키는게 목적이다.

opener를 알아보자

<!-- noopener없이 B를 새창에서 여는 취약한 링크를 가지고 있는 A 페이지 --> <a link="https://www.B.com" target="_blank">취약한 링크! </a>
HTML
복사
A 페이지에서 해당 링크 클릭 시 새창에 B 페이지가 열린다.
// www.B.com // A로 부터 새창에 열린 B 페이지 // 새로운 창에 B 페이지의 브라우저 개발자 도구 콘솔에서 실행 시 window.opener; // null
JavaScript
복사
부모 창의 global 객체가 참조되지않.는.다.
나를 open한 부모 창의(A 페이지) global 객체가 나와야 하는데.. 왜 null이 나오는거지?
수많은 블로그 글들이 잘못된것인가? 아님 내가 모자란것인가?
진지하게 이 생각 15초 정도 함…

2-1. Today’s 통수

safari는 2019년 부터, chrome은 2021년 초반 부터 추가된 기능으로 a 태그에 rel=noopener를 명시적으로 적어주지 않아도 noopener attribute가 자동으로 적용된다.
오히려 a 태그에 rel=opener를 명시적으로 입력해줘야 window.opener로 부모 브라우저의 global 객체에 접근할 수 있다.
아무값이 안들어가면 noopener가 attribute가 자동으로 적용된다.

패치버전

크롬 88.0.4324.96 버전 → 2021년 1월 19일에 업데이트 됐으며 새 창을 여는 링크의 경우 자동으로 noopener attribute가 적용된다.
사파리 11.1 → 2018년 3월에 noopener 사용 가능한 기능이 업데이트 됐으며 14.1버전에서 부터는 새 창을 여는경우 자동으로 noopener attribute가 적용된다.

3. 2 회차 시도

rel=”opener”로 바꿔보자

<!-- noopener없이 B를 새창에서 여는 취약한 링크를 가지고 있는 A 페이지 --> <a link="https://www.B.com" target="_blank" rel="opener"> 취약한 링크! </a>
HTML
복사
해당 링크 클릭 시 새로운창에 B 페이지가 뜬다.
// 새로운 창에 뜬 네이버에서 브라우저 개발자도구 콘솔에서 실행 시 window.opener; // global {window: global, self: global, location: {…}, closed: false, frames: global, …}
JavaScript
복사
현재 창을 열은 부모 창을 참조할 수 있는 전역 객체
드디어 부모 창의 전역 객체에 접근 가능 -> 부! 모! 창을 조작 할 수있다고!?!?!?!?!?
아직까진 이해가 잘되지않을 것 이다. 부모창 조작 가능의 힘을. 느껴라.

4. Reverse tabnabbing

A(부모) -> B(자식) 새로운 창에 열린 자식 창이 부모창을 다른 주소로 리다이렉트 시키는 공격 기법
부모 브라우저의 window 객체에 접근 가능하다 === 브라우저의 전역 객체를 건들 수 있다는 뜻
해당 취약점이 제일 많이 사용되는 케이스는 Reverse Tabnabbing -> 부모 사이트를 피싱 사이트로 재이동 시키기

이동 시키기가 그렇게 취약점이야?

디자인이 똑같은 피싱 사이트로 이동 시키면 이용자는 당연하게 생각하고 계정정보 입력 후 로그인을 시도한다.
하지만 계정 정보는 피싱 사이트를 만든 공격자의 서버로 이동되므로 이용자의 계정정보가 공격자에게 탈취된다.

공격 예시

graph LR
  A(A - 취약한 웹사이트)
  B(B - A에 링크로 존재하는 Reverse tabnabbing을 수행하는 공격 사이트)
  C(C - A를 따라 만든 피싱 사이트)
Mermaid
복사
sequenceDiagram
    participant A
    participant B
    A->>B: 1. A에서 링크를 눌러 Reverse tanabbing 수행하는 페이지로 이동
		B->>A: 2. B 페이지는 부모 윈도우(A)의 객체를 참조해 피싱 사이트인 C로 이동
		A->>C: 3. A가 피싱 페이지인 C로 이동됨
		Note over C: 4. A와 똑같이 생긴 C 페이지에서 <br/> 개인정보 입력 시<br/> 공격자는 피해자 개인정보 탈취
Mermaid
복사
1.
피해자가 A에서 서비스 이용 중
2.
A에 존재하는 링크를 타고 C 사이트로 이동
3.
C가 Reverse tabnabbing을 수행하여 A 부모 페이지에 존재하던 A를 B로 리다이렉트한다.
A를 리다이렉트 하는 C 코드
<html> <body> <script> if (window.opener) { window.opener.location = "C 주소"; } </script> </body> </html>
HTML
복사
4.
피해자는 B에서 로그인 정보 입력하여 로그인 시도
5.
공격자가 피해자 로그인 정보 획득
이용자의 계정 정보는 성공적으로 공격자에게 탈취됐다.

5. noreferrer

noopener와 쌍둥이로 같이 자주쓰이는 링크의 rel(ationship)attribute
http 헤더에 referer 속성을 없애주는 attribute

http Referer 헤더

해당 페이지를 방문하는 이용자가 어느 페이지에서 방문한건지 추적할 수 있게 해주는 헤더
a 태그에 noreferrer를 사용 시 Referer 헤더가 사라져 해당 사이트에서 이전 사이트 추적이 불가능해짐
referrer이 정확한 맞춤법이고 referer는 original spec의 오타였다. 이게..맞나 싶지만 그런일이 일어나서 아직도 referer로 굳어져 왔다.(이게 말이되낰ㅋㅋㅋ) → 아래 MDN 링크에서 참조할 것.

사용이유

noopener를 지원하지 않는 브라우저에서는 noreferrer로 작성 시 noopener가 해결하는 문제를 대신해서 해결해준다.
항상 둘이 붙어서 한세트로 소환되는 이유를 한마디로 말하자면 noopener보다 더 나은 호환성이다.
대표적인 예로 IE11에서는 noopener 속성이 존재하지 않는다. → 하지만 noreferrer가 있을 경우 noopener처럼 작동한다.
이외 (구버전) 브라우저들도 noopener보다 noreferrer를 더 많이 지원한다.

6. 결론

브라우저 사용자 → 브라우저 최신 버전을 쓰자 개발자 → 모든 이용자가 최신 브라우저를 사용한다는 망각에서 빠져나와서 개발하자
최신 버전의 브라우저에선 억지로 터지게 만들지 않는 이상 터지지 않는 취약점이다.
새 창을 여는 링크에 해당 attribute가 빠질 경우 Reverse Tabnabbing 취약점을 이용해 이용자를 피싱 사이트로 유도 할 수 있다.
noreferrer는 noopener 보다 더 호환성이 좋다. 하위호환을 위해 이왕 쓰는거 같이 써주자.

서비스 이용자들은 어떻게 해야할까

브라우저 버전 최신으로 업데이트 하고 편하게 사용하면 OK
는 너무 수동적인 자세, 취약점은 어디든 존재한다. 항상.
조금만 낌세가 이상하더라도 “이 URL이 맞는 URL가?”라는 질문을 항상하자.

개발자들은 어떻게 해야할까

모든 이용자가 최신 브라우저를 사용하는게 아니니 a태그에서 새로운 창으로 열때는 rel=noopener noreferrer를 꼭 추가하자.

참조