avatar
Published on

Referer와 Referer-Policy를 위한 가이드

Author
  • avatar
    Name
    yceffort

Table of Contents

same-site와 same-origin의 차이

Origin

https://yceffort.kr:443

https://yceffort.kr:443 를 기준으로 비교했을 때,

Origin비교결과이유
https://fake.kr:443cross-origin도메인이 다르다.
https://www.yceffort.kr:443cross-origin서브도메인이 다르다.
https://blog.yceffort.kr:443cross-origin서브도메인이 다르다.
http://yceffort.kr:443cross-originscheme이 다르다.
http://yceffort.kr:80cross-originport가 다르다.
https://yceffort.kr:443same-origin완전히 같다.
https://yceffort.krsame-origin포트가 없지만, https의 기본포트 443이 있다고 간주한다.

Site

탑 레벨 도메인 (TLD), 즉 .com.org등은 Root Zone Database에 등록되어 있다. 내 블로그 주소를 기준으로, site는 TLD와 domain의 조합이다. 따라서 내 블로그의 siteyceffort.kr이다.

그러나, .co.kr이나 .github.io와 같은 주소도 더러 있는 것을 볼 수 있다. 이들의 TLD는 .kr .io인데, 단순히 TLD만으로 이들의 도메인을 결정할 수 있는 방법이 없다. 그래서 eTLD (effective Top Level Domain) 리스트가 만들어졌다. eTLD

예를 들어, 내 구 블로그 주소인 https://yceffort.github.io 를 기준으로 살펴보자.

  • TLD: .io
  • eTLD: .github.io
  • eTLD+1: yceffort.github.io (site)

https://yceffort.kr:443 를 기준으로 비교했을 때,

Origin비교결과이유
https://fake.kr:443cross-site도메인이 다르다.
https://blog.yceffort.kr:443same-site서브도메인이 다르지만 상관없다.
http://yceffort.kr:443same-sitescheme이 다르지만 상관없다.
https://yceffort.kr:80same-siteport가 다르지만 상관없다.
https://yceffort.kr:443same-site완전히 같다.
https://yceffort.krsame-siteport가 없지만 상관없다.

위에서 보다시피 same-site는 scheme를 무시하고 있지만, http의 취약점을 방어하기 위해 조금 더 엄격한 방식으로 구별하는 방식이 있다. 이를 schemeful same-site라고 한다. 이 경우 http://yceffort.kr과 https://yceffort.kr 는 스키마가 다르므로 다른 사이트로 취급한다.

Origin비교결과이유
https://fake.kr:443cross-site도메인이 다르다.
https://blog.yceffort.kr:443schemeful-same-site서브도메인이 다르지만 상관없다.
http://yceffort.kr:443cross-sitescheme이 다르다.
https://yceffort.kr:80schemeful-same-siteport가 다르지만 상관없다.
https://yceffort.kr:443same-site완전히 같다.
https://yceffort.krschemeful-same-siteport가 없지만 상관없다.

Referer와 Referrer-Policy 101

맨 처음 포스팅을 할 때 이상하다고 느낀 것은 Referer와 Referrer-policy에서 Referrer의 스펠링이 다른 것이었다. (틀리다고 계속 에러메시지가 떴다.) 알고보니 오타가 고대로 스펙이 되버린 것이었다.

The misspelling of referrer originated in the original proposal by computer scientist Phillip Hallam-Baker to incorporate the field into the HTTP specification.[4] The misspelling was set in stone by the time of its incorporation into the Request for Comments standards document RFC 1945; document co-author Roy Fielding has remarked that neither "referrer" nor the misspelling "referer" were recognized by the standard Unix spell checker of the period.[5] "Referer" has since become a widely used spelling in the industry when discussing HTTP referrers; usage of the misspelling is not universal, though, as the correct spelling "referrer" is used in some web specifications such as the Document Object Model.

https://en.wikipedia.org/wiki/HTTP_referer

http 요청은 옵셔널 헤더인 Referer를 가지고 있을 수 있다. 이 정보는 이 요청이 만들어진 origin 또는 웹페이지 URL을 가리킨다. Referrer-Policy헤더는 요청과 함께 얼마나 많은 레퍼럴 정보를 포함해야 하는지 알려준다.

아래 예제를 보자.

https://web-dev.imgix.net/image/admin/cXgqJfmD5OPdzqXl9RNt.jpg

Referer 헤더에 해당 정보를 요청한 사이트의 전체 주소가 담겨져 있다.

Referer 헤더는 다양한 형태의 요청에 존재할 수 있는데, 예를 들어

  • 사용자가 링크를 클릭하는 네비게이션 링크
  • 브라우저가 이미지, iframe, script 등 페이지에 필요한 리소스를 요청하는 subresource 요청

가 있다. 네비게이션과 iframe의 경우, 자바스크립트의 document.referrer를 이용해서도 동일한 정보에 접근할 수 있다.

Referer는 꽤나 유용한 정보가 될 수 있다. 옐르 들어, site-two.example의 사용자중 50%는 social-network.example에서 왔다는 것을 파악할 수 있다.

그러나, query와 path를 포함한 전체 주소를 Referer를 통해서 다른 origin에 보내는 것은, 보안 상에서 문제가 될 수 있다. 아래의 예를 살펴보자.

https://web-dev.imgix.net/image/admin/oTUtfrwaGYYjlOJ6KRs6.jpg?auto=format&w=1600

1번과 5번 예제에서 볼 수 있다시피 이 사이트에 온 사람이 누구인지 식별할 수도 있게 되어 버린다. 6번의 경우에는 극단적이지만 끔찍한 예제이다. 💀

따라서, 사이트의 요청에 사용할 수 있는 referer 데이터를 제한하기 위해서 사용하는 것이 Referrer-Policy이다.

어떠한 것들이 가능하고, 차이는 무엇일까?

가능한 정책은 총 8가지다. 정책에 따라서, Referer의 데이터는

일부 정책의 경우 context에 따라서 다르게 작동하도록 동작 되어있다. (cross-origin, same-origin request, security) 이는 사이트 내에서 Referer를 유지하면서, 동시에 다른 origin에서는 정보를 제한하는데 있어서 유용하다.

No DataOrigin OnlyFull URL
no-referrer
origin
unsafe-url
strict-originHTTPS → HTTPHTTPS → HTTPS, HTTP → HTTP
no-referrer-when-downgradeHTTPS → HTTPHTTPS → HTTPS, HTTP → HTTP
origin-when-cross-origincross-originsame-origin
same-origincross-originsame-origin
strict-origin-when-cross-originHTTPS → HTTPcross-origin, HTTPS → HTTPS, HTTP → HTTP

실제 예제 까지 보고 싶다면 여기를 참고

  • scheme를 보는 모든 정책 (strict-origin no-referrer-when-downgrade strict-origin-when-cross-origin)의 경우에, HTTP가 실제로 더 보안에 취약함에도 불구하고, HTTP origin에서 다른 HTTP origin으로 가는 것을 HTTPS origin에서 다른 HTTPS origin으로 가는 것과 동일하게 취급한다. (= HTTP와 HTTPS에 대해 차이를 두고 있지 않다.) 이러한 정책의 경우 중요한 것은, 보안 다운그레이드가 발생하는지 여부, 즉 암호화된 원본에서 암호화되지 않은 원본으로 데이터를 노출할 수 있는지 여부이다. HTTP에서 HTTP는 암호화가 없어서 다운그레이드 되지 않는다. 다만 HTTPS에서 HTTP는 다운그레이드가 나타난다. (암호화가 된 것에서 암호화가 안된 것으로 가므로)

  • 요청이 same-origin이라면, 이 뜻은 scheme (HTTS, HTTP)가 같다는 뜻이다. 따라서 보안에서 다운그레이드가 이루어지지 않는다.

브라우저별 표준

만약 referrer-policy가 설정되어 있지 않다면, 브라우저 기본 정책이 적용된다.

브라우저기본 정책
Chrome85 버전부터 no-referrer-when-downgrade에서 strict-origin-when-cross-origin으로 변경
Firefoxno-referrer-when-downgrade, 시크릿 모드에서는 strict-origin-when-cross-origin
Edgeno-referrer-when-downgrade
Safaristrict-origin-when-cross-origin와 비슷하게 동작

referrer policy 설정하는 올바른 방법

사이트에 referrer policy를 설정하는 방법은 여러가지가 있다.

페이지마다, 요청마다 다른 정책을 쓸 수 있다는 것을 의미한다. HTTP header와 meta 엘리먼트는 모두 페이지 레벨에서 동작한다. 유효 정책을 정하는 순위는 아래와 같다.

  • element 레벨
  • page 레벨
  • 브라우저 기본값

예제

<meta name="referrer" content="strict-origin-when-cross-origin" />
<img src="..." referrerpolicy="no-referrer-when-downgrade" />

이 경우 이미지는 no-referrer-when-downgrade 정책으로 가게 된다.

referrer policy를 보는 법

브라우저의 네트워크 탭을 보면 된다.

Referrer policy example

어떤 정책이 좋을까?

요약: 명시적으로 보안이 강화된 strict-origin-when-cross-origin를 사용하는 것이 좋다.

왜 명시적으로 써야 할까?

referrer policy가 제공되지 않는다면, 브라우저 기본 정책이 사용된다. 사실, 많은 웹사이트 들이 이러한 정책을 브라우저 기본값에 의존하는데 이는 좋지 않다. 그 이유는

  • 브라우저 모드 (시크릿모드 같이)에 따라서 no-referrer-when-downgrade이거나 strict-origin-when-cross-origin일 수 있는데, 이는 웹사이트에서 일관된 동작을 하지 못하도록 막는다.
  • 브라우저의 기본값인 strict-origin-when-cross-origin는 cross-origin 요청에 대해서 referrer를 trimming하는 기능으르 가지고 있다. (파이어폭스, 사파리의 경우에만 그렇다. [여기](Referrer trimming)를 참조) 명시적으로 정책을 선언해서 이러한 행위를 막을 수 있다.

strict-origin-when-cross-origin인가?

  • 안전하다: 웹사이트가 https 일 경우, https 가 아닌 요청에 대해서 웹사이트 주소를 노출하고 싶지 않을 것이다. 만약 누구라도 네트워크에서 이런 정보를 본다면, 유저의 정보가 중간자 공격 의 위험에 노출되게 한다. no-referrer-when-downgrade strict-origin-when-cross-origin no-referrer strict-origin로 막을 수 있다.
  • 개인정보 보안: cross-origin 요청의 경우, no-referrer-when-downgrade는 모든 주소를 노출시킨다. strict-origin-when-cross-originstrict-originorigin만 공유하고, no-referrer의 경우에는 아무정보도 안나타나게 된다.
  • 용이하다: no-referrerstrict-origin은 절대로 전체 URL을 공유하지 않는다. 근데 문제는 same-orgin일 때도 공유를 안한다는 것. 이를 피하기 위해서는 strict-origin-when-cross-origin를 쓰면 된다.

따라서 모든 경우에 있어서 strict-origin-when-cross-origin가 가장 최선의 선택이라고 볼 수 있다.

<meta name="referrer" content="strict-origin-when-cross-origin" />

혹은 서버사이드에서

const helmet = require('helmet')
app.use(helmet.referrerPolicy({ policy: 'strict-origin-when-cross-origin' }))

만약 예외가 필요하다면

별도로 element 나 요청별로 예외를 두는 것이 좋다. 그럼에도, unsafe-url 같은 건 안쓰는게 좋다.

<meta name="referrer" content="strict-origin-when-cross-origin" />
<img src="" referrerpolicy="no-referrer-when-downgrade" />
fetch(url, { referrerPolicy: 'no-referrer-when-downgrade' })

element 별로 정책을 주는 것도 모든 브라우저에서 되는 것은 아니다. 참고

외부에서 오는 요청에 referrer를 활용하는 법

Cross Ste Request Forgery (CSRF) 보호

CSRF 방어를 위해서 referrer를 쓰는 것은 몇가지 허점이 있다.

  • no-referrer나 request를 도용하는 경우 아무런 데이터를 볼 수 없을 수 있다. 요청의 헤더에 대한 제어를 하고 있지 못한다면, 요청에 안전한 헤더가 온다는 보장이 없다.
  • Referer (document.referer) 에는 원하는 것 (단순히 cross-origin만 알고 싶었는데..) 보다 더 많은 양의 데이터가 들어 있을 수 있다.

CSRF 방어를 위해서는 CSRF Token을 사용하는 것을 추천한다.

로깅

Referer에는 개인정보가 담겨 있을 수 있으므로, 다루는데 신중해야 한다. Referer를 사용하는 대신에, Origin이나 Sec-Fetch-Site를 써보는 것도 좋다.

sec-fetch-site는 지원이 제한적이므로, origin을 쓰는게 낫다.

결제

결제 사업자는 보안 체크를 위해, 들어오는 요청에 대해서 Referer를 확인할 수도 있다. 예를 들어

  • 유저가 online-shop.example/cart/checkout 에서 결제 버튼을 누른다.
  • online-shop.example 가 결제를 위해 payment-provider.example로 리다이렉트 시킨다.
  • payment-provider.example가 Referer를 확인하여 허가된 사이트로 부터 온 요청인지 확인한다. 그렇지 않다면, 결제 요청을 거부한다.

결제 플로우에서 보안 체크

결제사업자가 Referer를 체크하는 것은 기본적인 방어 체계가 될 수 있다. 그러나 반드시, 또 다른 방어체계를 마련해 두어야 한다.

Referer만으로는 모든 것을 막기에 완벽하지 않다. 만약 결제 사이트에서 no-referrer를 설정해 두었다면, 해당 정보를 확인할 수 없다. 그러나, 결제 제공 사업자로서, 일단 Referer를 본다면 해당 정보가 있는지 없는지 정도 수준의 기본적인 체크는 할수가 있다.

  • Referer가 언제나 있을 거라고 기대하지마라. 설령 존재한다 하더라도, 이는 아주 기초적인 점검 항목중 하나인 origin만 살펴볼 수 있다. Referer 허용 값을 작성할때, origin만 있도록 하는 것이 중요하다. 즉, online-shop.example/cart/checkout가 아닌 online-shop.example여야 한다는 것이다. 이는 결제 사이트에 따라서 정책이 다르게 설계 될 수 있으므로 (=꼭 FULL URL이 온다는 보장은 없으므로) 반드시 origin만 확인해야 한다.
  • 만약 Referer가 없거나, 기본적인 점검이 통과했을 경우, 아래의 추가적인 항목으로 검사를 시도해야 한다.

더 안전한 방법

한 가지 신뢰할 수 있는 검증 방법은 요청자가 요청한 매개변수를 고유한 키와 함께 해시하여 보내도록 하는 것이다. 결제 제공자로서 이 해시 값을 검사할 수 있고 이 값이 일치하는 요청에 대해서만 받으면 된다.