2022-05-11-TIL
2022-05-11-TIL
Today I Learned
CORS
CORS(Cross-Origin Resource Sharing)는 브라우저가 다른 출처(origin)의 리소스를 요청할 때, 서버가 응답 헤더로 허용 여부를 알려 주는 HTTP 헤더 기반 메커니즘이다. 출처는 스킴, 도메인, 포트 세 가지로 정해지고 경로는 포함하지 않는다. https://a.example.com에서 https://api.example.com을 호출하면 도메인이 달라서 교차 출처 요청이 된다.
핵심은 이 검사를 브라우저가 한다는 점이다. 서버는 요청을 정상적으로 처리하고 응답도 보내지만, 응답에 Access-Control-Allow-Origin 같은 헤더가 없거나 요청 출처와 맞지 않으면 브라우저가 JavaScript에 응답을 넘겨주지 않는다. 그래서 같은 API를 curl이나 서버 간 호출로 부르면 문제가 없고 브라우저에서만 에러가 난다. 링크의 Stack Overflow 질문도 이 지점을 다룬다. 정책을 강제하는 것은 클라이언트(브라우저)이고, 허용 범위를 결정하는 것은 서버다.
자격 증명(쿠키, Authorization 헤더)을 함께 보내는 요청이라면 서버는 Access-Control-Allow-Origin: *를 쓸 수 없고 정확한 출처를 적어야 하며, Access-Control-Allow-Credentials: true도 함께 보내야 한다(MDN: CORS).
AWS 링크들은 S3와 CloudFront에서 CORS 헤더를 붙이는 방법을 다룬다. S3는 버킷에 CORS 설정(JSON)을 두고, CloudFront는 응답 헤더 정책이나 CloudFront Functions로 응답에 헤더를 추가할 수 있다. 정적 리소스를 CDN 뒤에서 서빙한다면 원본과 CDN 중 어느 쪽에서 헤더를 붙일지 정하고, 그 설정이 실제 응답에 남는지 확인해야 한다.
- https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
- https://xiubindev.tistory.com/115
- https://aws.amazon.com/ko/premiumsupport/knowledge-center/no-access-control-allow-origin-error/
- https://aws.amazon.com/ko/about-aws/whats-new/2021/11/amazon-cloudfront-supports-cors-security-custom-http-response-headers/
- https://docs.aws.amazon.com/AmazonS3/latest/userguide/cors.html
- https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/example-function-add-cors-header-response.html
- https://livebook.manning.com/book/cors-in-action/chapter-4/5
- https://stackoverflow.com/questions/36958999/cors-is-it-a-client-side-thing-a-server-side-thing-or-a-transport-level-thin
- https://www.howtogeek.com/devops/handling-cors-in-web-applications/
Sec-Fetch-Dest
Sec-Fetch-Dest는 Fetch Metadata 요청 헤더 중 하나로, 이 요청의 결과가 어디에 쓰일지(destination)를 서버에 알려 준다. 값으로는 document(페이지 이동), script, style, image, font, iframe, empty(fetch()나 XHR 호출) 등이 있다. Sec- 접두사가 붙은 금지된 헤더라서 JavaScript가 값을 바꿀 수 없고 브라우저가 직접 붙인다.
함께 오는 헤더로 Sec-Fetch-Site(같은 출처인지, 교차 사이트인지), Sec-Fetch-Mode(cors, no-cors, navigate), Sec-Fetch-User(사용자 동작으로 일어난 이동인지)가 있다. 서버는 이 값들로 “교차 사이트에서 온 요청인데 목적지가 script라면 거부” 같은 리소스 격리 정책을 만들어 CSRF나 XSSI 같은 공격면을 줄일 수 있다(MDN: Sec-Fetch-Dest).
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Dest
- https://web.dev/fetch-metadata/
- https://docs.w3cub.com/http/headers/sec-fetch-dest
- https://www.w3.org/TR/fetch-metadata/#sec-fetch-dest-header
Preflight 요청
모든 교차 출처 요청이 바로 전송되는 것은 아니다. 다음 조건을 모두 만족하는 단순 요청(simple request)만 바로 보내진다.
- 메서드가
GET,HEAD,POST중 하나 - 직접 지정한 헤더가 CORS 안전 목록(
Accept,Accept-Language,Content-Language,Content-Type,Range)뿐 Content-Type이application/x-www-form-urlencoded,multipart/form-data,text/plain중 하나
이 조건을 벗어나면, 예를 들어 Content-Type: application/json으로 POST하거나 Authorization 헤더를 붙이면, 브라우저는 먼저 OPTIONS 메서드로 preflight 요청을 보낸다.
1
2
3
4
OPTIONS /api/orders HTTP/1.1
Origin: https://front.example.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: content-type, authorization
서버가 Access-Control-Allow-Methods, Access-Control-Allow-Headers로 허용 범위를 답하면 그때 본 요청이 나간다. Access-Control-Max-Age를 주면 preflight 결과를 그 시간(초) 동안 캐시해서 매 요청마다 OPTIONS가 나가는 것을 줄일 수 있다. 서버나 게이트웨이가 OPTIONS 요청을 인증 필터에서 막아 버리면 본 요청이 아예 나가지 않으므로, 인증보다 CORS 처리가 앞서도록 설정해야 한다.
- https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
- https://jsikim1.tistory.com/184
댓글
아직 댓글이 없습니다