01DEFINITION
한 줄로 말하면
공격자가 자기 페이지에 우리 사이트를 투명한 iframe 으로 얹고 그 위에 가짜 버튼을 겹칩니다. 방문자는 자기가 무엇을 누르는지 모른 채, 로그인된 상태로 우리 사이트에서 어떤 동작을 실행하게 됩니다. 이것이 클릭재킹입니다. X-Frame-Options 한 줄이면 막히고, 요즘은 CSP 의 frame-ancestors 로 어느 도메인까지 허용할지 더 정밀하게 정합니다.
02RISK
없으면 무슨 일이 생기나
공격자는 그럴듯한 이벤트 페이지를 하나 만듭니다. 화면에는 응모하기 버튼이 있고, 그 버튼 바로 위에 우리 사이트가 투명한 창으로 겹쳐 있습니다. 방문자가 응모하기를 누르는 순간 실제로 눌리는 것은 우리 사이트의 어떤 버튼입니다. 방문자는 이미 우리 사이트에 로그인해 있으므로 그 동작은 정상적으로 처리됩니다.
무엇이 눌리느냐가 피해의 크기를 정합니다. 회원 탈퇴, 권한 위임 동의, 결제 승인, 게시글 삭제처럼 한 번 누르면 되는 동작이 표적이 됩니다. 우리 서버의 로그에는 정상 로그인 사용자가 정상적으로 요청한 것으로 남기 때문에, 사후에 무엇이 잘못됐는지 가려내기도 어렵습니다.
방문자 입장에서 방어할 방법이 사실상 없다는 점도 문제입니다. 주소를 확인해도 자기가 보고 있는 것은 공격자의 페이지이고, 우리 사이트는 눈에 보이지 않는 층에 있습니다. 이 항목이 서버 설정 한 줄인데도 심각하게 다뤄지는 이유입니다.
03CHECK IT YOURSELF
직접 확인하는 방법
아래는 저희를 거치지 않고 지금 해 보실 수 있는 방법입니다. 결과를 저희에게 보내실 필요도 없습니다.
-
01
두 헤더 중 하나라도 있는지 본다
X-Frame-Options 와 CSP 의 frame-ancestors 는 같은 일을 합니다. 둘 다 없으면 이 항목에 걸립니다. 최신 브라우저는 둘 다 있을 경우 frame-ancestors 를 따릅니다.
$ curl -sI https://example.com/ | grep -iE "x-frame-options|content-security-policy" x-frame-options: SAMEORIGIN
-
02
직접 iframe 에 넣어 본다
로컬에 아래 내용으로 파일 하나를 만들어 브라우저로 엽니다. 우리 사이트가 그려지면 막혀 있지 않은 것이고, 빈 화면이나 차단 메시지가 나오면 정상입니다. 개발자도구 콘솔에도 차단 사유가 남습니다.
<iframe src="https://example.com/" width="600" height="400"></iframe>
-
03
홈이 아니라 동작이 있는 페이지로 본다
표적이 되는 것은 버튼이 있는 페이지입니다. 회원정보 수정, 주문 확인, 관리자 화면처럼 한 번의 클릭으로 무언가 바뀌는 주소를 확인하십시오. 홈에만 헤더가 붙어 있는 설정이 있습니다.
04HOW TO FIX
고치는 방법
서버 환경에 따라 적을 자리가 다릅니다. 아래는 가장 흔한 구성 기준이고, 적용 전에 현재 설정을 백업하십시오.
대부분의 사이트에 맞는 설정입니다. 우리 페이지가 우리 페이지 안에서 창으로 열리는 것은 허용하고, 외부 도메인에서는 막습니다.
Header always set X-Frame-Options "SAMEORIGIN"
이쪽이 최신 방식이고 허용 도메인을 정밀하게 지정할 수 있습니다. 창을 아예 허용하지 않으려면 none 으로 둡니다.
Header always set Content-Security-Policy "frame-ancestors 'self'" # 특정 파트너만 허용하는 경우 # frame-ancestors 'self' https://partner.example.com
결제 모듈, 지도, 외부 위젯으로 제공하는 페이지는 다른 사이트 안에서 열려야 정상입니다. 이런 경로는 전역 정책에서 빼고 별도 헤더를 적용하십시오. 전부 막았다가 결제가 안 되는 사고가 이 항목에서 나옵니다.
<Location "/widget/">
Header always unset X-Frame-Options
Header always set Content-Security-Policy "frame-ancestors https://partner.example.com"
</Location> 05FAQ
자주 묻는 질문
우리 사이트는 로그인 기능이 없는데도 필요한가요
- 위험은 줄지만 0 은 아닙니다. 로그인이 없어도 문의 폼 제출, 설문 응답, 외부 링크 유도처럼 한 번의 클릭으로 일어나는 동작은 그대로 남습니다. 또 우리 페이지를 그대로 감싸 자기 콘텐츠처럼 보여 주는 것 자체를 막는 효과도 있습니다.
SAMEORIGIN 과 DENY 중 무엇을 써야 하나요
- 사이트 안에서 iframe 을 전혀 쓰지 않는다면 DENY 가 더 안전합니다. 다만 게시판 에디터의 미리보기, 관리자 화면의 일부 기능이 내부적으로 iframe 을 쓰는 경우가 있어 확인이 필요합니다. 확신이 없으면 SAMEORIGIN 으로 시작하십시오.
X-Frame-Options 와 CSP 를 둘 다 넣어도 되나요
- 둘 다 넣어도 충돌하지 않습니다. 최신 브라우저는 frame-ancestors 를 우선하고, 지원하지 않는 옛 브라우저는 X-Frame-Options 를 따릅니다. 두 값이 서로 다른 정책을 말하지 않게만 맞춰 두시면 됩니다.
이 헤더를 켜면 네이버나 카카오 공유에 영향이 있나요
- 없습니다. 공유 미리보기는 페이지를 창으로 여는 것이 아니라 서버가 직접 읽어 OG 태그를 뽑는 방식이라 이 헤더의 영향을 받지 않습니다. 영향을 받는 것은 다른 사이트가 우리 페이지를 화면 안에 끼워 넣어 보여 주는 경우뿐입니다.
GET STARTED
내 사이트는
이 항목을 통과할까요
주소만 넣으시면 이 항목을 포함해 25가지를 판정해 점수와 실패 개수를 보여 드립니다. 로그인도, 소유 확인도 필요 없습니다 — 전부 밖에서 관찰만 하는 항목이기 때문입니다.