본문 바로가기
spring & boot/Spring & Spring Boot

[Spring Security] x-frame-option 헤더 설정(iframe)

by lucas_owner 2024. 4. 3.

 

목차

 

* 도메인 ~ 에서 연결을 거부 했습니다 (net::ERR_BLOCKED_BY_RESPONSE)

최근 같은 그룹사의 다른 도메인에서 iframe 으로 내가 관리하는 도메인의 화면을 호출 하는 작업이 있었다. 

 

하지만, 그룹사 서버 배포 이후 ERR_BLOCKED_BY_RESPONSE 등의 오류 메시지가 출력되며, 해당 화면이 노출 되지 않는 문제가 발생했다. 

 

관련된 내용들을 알아보고, 어떤 흐름으로 해결방법을 찾았는지 기술하겠다.

 

단순 적용 방법이 급한 분은 목차의 '해결방법' 클릭하세요.

 

 

 

1.  iframe 이란 

우선 iframe 에 대해서 알아야 할것 같다. 

 

iframe 이란?

웹 페이지 안에 또 다른 웹페이지를 삽입하는것.

 

최근에는 iframe 이 많이 사라지는 추세이지만, 꽤나 많이 자주 보이는 기술이기도 하다. 

흔하게 볼 수 있는 것 중 하나인 '구글 맵' 으로 예를 들어 보면, 빨간 박스내부에 있는 주소를 복사한 후 

width, height 값을 설정한 뒤, html 웹 페이지 안에 삽입하는 방식이다.

 

html 4.01 에서 등장한 iframe은 별다른 제한 없이 웹페이지를 불러와서 삽입 할 수 있기에, 편리하고 페이지 하나에

수많은 페이지를 표현할 수 있는 장점이 있는데 왜 점점 사라지는 추세일까?

구글맵 iframe 예시

 

 

2. 해킹 공격

ClickJacking (클릭재킹) 

- iframe '클릭재킹' 이라는 해킹 공격에 취약한 보안취약점으로 구분되며

 

해킹의 방식 사용자가 특정한 웹페이지를 클릭하지만, 사실은 실제 컨텐츠는 숨겨져 있고 공격에 사용되는 html 내 요소를 클릭하게 되는것이다. 

즉 정상적인 동작처럼 보여지게 하여 사용자의 정보를 갈취 하거나, 사용자를 속이는 공격인것이다. 

 

실제 사용된 예시로는, 웹캠, 마이크를 활성화 시킨다거나, SNS의 '좋아요' 클릭, '링크 공유' 등이 있다. (페이스북, 플래시)

옳은 방법으로 사용한다면, 편한 기술이겠지만 이처럼 악의적인 마음을 가지고 사용한다면 보안에 치명적인 기술이 되기에 

HTML5 에서는 제외 혹은 사용자제가 되었다.

 

이러한 보안 이슈 때문에, 서버에서 X-Frame-Options 설정을 해제할 수 없었다.

 

3. 방어 방법

Spring Security 에서는 기본적으로 X-Frame-Options Click Jacking 공격을 막는 설정이 되어있다.

혹은 CORS 가 생기게 된 계기였다. CORS 는 Cross Origin Resource Sharing 으로 도메인이 다른 서버로 부터 resource 를 받을 수 있도록 허용해주는 방법이 생기게 된 것이였다. 규정을 지킨 도메인 <-> 서버 끼리 허용 하는 방식이기 때문에 비교적 안전한 방법이었지만 

iframe 에서 사용하던 방식에 비해, 불편하고 제약이 많은 방식 이었다. 

그렇게 생겨나게 된 방법이 http header 조작이다. 

 

X-Frame-Options

 

Http Header 보안헤더 중 하나인 X-Frame-Options 는 몇가지의 값을 통해, 부분 허용이 가능해졌다. 

 

X-Frame-Options 헤더는 브라우저에서 렌더링을 허용, 금지 여부를 결정하는 응답헤더이다. 

사용 가능한 옵션은 아래와 같다.

 

  • DENY : iframe 비허용(불가)
  • SAMEORIGIN : 동일 도메인 내에선 접근 가능
  • ALLOW-FROM {도메인} : 특정 도메인 접근 가능

* Spring Security 에서 사용되는 옵션은 다음 항목에서.

 

그럼 우리가 원하는 특정 도메인 허용 을 하기위해선 ALLOW-FROM 을 사용하면 되지않을까 ?

안타깝게도 ALLOW-FROM 옵션은 대부분의 브라우저에서 허용 되지 않는다. 

그렇기에, 다른 방법을 생각해야만 했었다.

 

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options

 

X-Frame-Options - HTTP | MDN

The X-Frame-Options HTTP response header can be used to indicate whether a browser should be allowed to render a page in a <frame>, <iframe>, <embed> or <object>. Sites can use this to avoid click-jacking attacks, by ensuring that their content is not embe

developer.mozilla.org

 

 

4. 해결 방법

- Content-Security-Policy 라는 헤더를 사용하는 방법이다. 

MDN 사이트에도 나와 있지만, "frame-ancestors" 라는 키워드를 같이 사용해야 한다.

 

많은 예제들을 살펴보게 된다면, Apache, Nginx 설정 변경이 존재한다. 

하지만 담당하던 도메인은 Apache 를 사용했지만, header 옵션에 대한 추가 설정이 되어있지않았다.(시큐리티를 통해설정...)

그래서 Security 내 설정을 통해 부분허용하는 방법을 찾을 수 밖에 없었다.

 

*Apache 

# 옵션 중 하나 선택.

Header set X-Frame-Options "deny"

Header always set X-Frame-Options "sameorigin"

Header set X-Frame-Options "allow-from https://example.com/"

 

* Nginx

add_header X-Frame-Options sameorigin;

 


 

* Spring Security 옵션

  • deny() : 허용하지 않음
  • sameOrigin() : 동일 도메인내 허용
  • disable() : x-frame-options 사용 X 

 

자 우선 Security Config 의 설정을 일부 확인해보자.

* deny

...

// deny()
and()
  .headers()
  .frameOptions()
  .deny()

...

- deny() 옵션은 default 설정이며 위처럼 표현할 수 있다. 

 

* Same Origin

...

// sameOrigin
and()
  .headers()
  .frameOptions()
  .sameOrigin()

...

 

 

* ALLOW-FROM

...

// allow from
and()
  .headers()
  .frameOptions()
  .addHeaderWriter(new StaticHeadersWriter("X-FRAME-OPTIONS" 
     ,"ALLOW-FROM https://permitted-site.example.com") 
   // X-Frame-Options ALLOW-FROM
   
...

- 하지만 ALLOW-FROM 의 경우 대부분의 브라우저에서 지원하지 않기에, 선택지에 존재하지 않는다.

 

 

* Content Security Policy 

...

.and()
  .headers()
   .contentSecurityPolicy("frame-ancestors 'self' https://*.example.com")
  and()
  .frameOptions()
  .deny()

...

- 해당 옵션을 사용한다면

 

자기 자신(same-origin), 지정 도메인은 iframe 허용, 그 외 나머지 사이트에서는 비허용

하는 옵션이 된다. 

이렇게 되면, 수많은 브라우저에서 지원도 가능하게 되고, 원하는 사이트와 같은 도메인에서의 iframe 을 허용하게 함으로써

iframe 을 좀더 잘 사용할 수 있는 방법이 되지 않을까 싶다.

 

- 필자의 저 옵션의 경우, 요청하는 도메인이 서브 도메인이었기 때문에 아무 이상 없이 동작했지만

서브도메인, 자기자신 이 아닌 도메인에서 호출했을때 가능한지에 대해서는 추가적인 테스트를 해봐야 할것 같다.

 

 

 

 

동작이 되지 않거나, 다른 좋은 방법이 존재한다면 언제든 알려주시면 감사하겠습니다~! 

반응형

댓글