CORS 동작 원리와 해결 방법 — CSRF와 차이점
security
개념
브라우저의 보안 정책. 다른 출처(origin)에서 온 요청을 기본적으로 차단한다.
출처(origin) = 프로토콜 + 도메인 + 포트. 하나라도 다르면 다른 출처.
https://my-app.com → https://api.my-app.com ❌ 도메인 다름
https://my-app.com → https://my-app.com:8080 ❌ 포트 다름
https://my-app.com → http://my-app.com ❌ 프로토콜 다름
https://my-app.com/a → https://my-app.com/b ✅ 경로만 다름
왜 차단하나
브라우저가 아무 사이트에서 다른 서버로 요청 보내는 걸 허용하면, 로그인된 쿠키가 같이 날아갈 수 있다. 그래서 기본은 차단이고, 서버가 명시적으로 "이 출처는 괜찮아"라고 응답해야 통과시킨다.
동작 방식
단순 요청 (Simple Request)
GET, POST(폼 전송 수준)처럼 단순한 요청은 바로 보내고, 응답의 Access-Control-Allow-Origin 헤더를 확인한다.
요청 → 서버 응답: Access-Control-Allow-Origin: https://my-app.com → 브라우저 통과
프리플라이트 (Preflight)
Content-Type: application/json이나 커스텀 헤더가 붙으면 브라우저가 먼저 OPTIONS 요청을 보내서 허용 여부를 확인한다.
1. OPTIONS /api/data (프리플라이트)
→ 서버: 허용할 출처, 메서드, 헤더 응답
2. 허용됐으면 실제 요청 전송
서버 응답 헤더
Access-Control-Allow-Origin: https://my-app.com
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: true
Allow-Origin: *은 전체 허용이지만Allow-Credentials: true와 동시에 못 씀- 쿠키를 보내려면 출처를 명시해야 함
CORS vs CSRF
이름이 비슷해서 헷갈리는데 관점이 다르다.
| CORS | [[XSS-CSRF|CSRF]] | |
|---|---|---|
| 뭐냐 | 브라우저의 차단 정책 | 공격 기법 |
| 누가 막냐 | 브라우저가 응답을 차단 | 서버가 토큰/SameSite로 검증 |
| 뭘 막냐 | 다른 출처에서 응답 읽는 것 | 다른 사이트에서 요청 위조하는 것 |
핵심: CORS는 응답을 읽지 못하게 막는 거지, 요청 자체를 막는 게 아니다. 그래서 CORS만으로는 CSRF를 막을 수 없다. 브라우저가 요청은 보내고 쿠키도 붙이되, 응답을 JS에서 못 읽게 할 뿐이다.
개발할 때 자주 만나는 상황
- 로컬에서
localhost:3000→localhost:8000요청 → 포트 달라서 CORS 에러 - 프록시로 우회하거나 서버에서
Allow-Origin설정 - [[Nginx]]에서 리버스 프록시로 같은 출처로 만들어버리는 것도 방법