1. 배경 개념1.1 origin1.2 same-origin1.3 cross-origin1.4 Same-origin policy 1.5 CORS2. Preflight3. Images4. CORS 요청, 응답에 사용되는 헤더4.1 요청 헤더4.2 응답 헤더
1. 배경 개념
1.1 origin
- origin = scheme + host + port
- scheme : URL의 프로토콜을 나타냄
- ex. http, https, ftp 등
- host : 서버의 도메인 이름 or IP 주소
- ex. localhost, www.naver.com
- port : 네트워크 포트
- 표준 포트가 아닐 경우 나타냄
- 포트가 URL에 없는 경우, origin = scheme + host
- ex.
8080
,3306
- 예시 : URL:
https://localhost:8080/surveys
- scheme:
https
- host:
localhost
- port:
8080
- path:
/surveys
1.2 same-origin
- 클라이언트가 서버에 요청을 보냈을 때
- 서버의 origin과 클라이언트의 origin이 동일한 경우
- 예시
https://aurora.net/home -> https://aurora.net/api/pos?id=290
https://aurora.net
으로 동일1.3 cross-origin
- 클라이언트가 서버에 요청을 보냈을 때
- 서버의 origin과 클라이언트의 origin이 다른 경우
- 예시
https://localhost:8080/home -> https://aurora.net/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:8080/api/pos?id=290
https://localhost:8080/home -> https://127.0.0.1:3000/api/pos?id=290
- 클라이언트의 origin :
https://localhost:8080
- 서버의 origin은 이와 다르므로 cross-origin
1.4 Same-origin policy
- 브라우저의 역할
- 클라이언트
- ex. 웹 페이지를 로드, 사용자와 상호작용, HTTP 요청을 서버에 보내고 응답을 처리
- 기본적으로 브라우저는 same origin의 HTTP 요청만 허용함
- Same origin policy(보안 정책)
- SOP가 필요한 이유 : 클라이언트와 서버를 보호하기 위해서
- 서버를 보호하는 관점
- 클라이언트가 악의적인 스크립트를 요청으로 보내는 것을 제한하기 위함
- CSRF (Cross-Site Request Forgery)
- 공격 대상: 주로 서버
- 공격 방식 : 사용자가 신뢰하는 웹 사이트에 이미 로그인된 상태 → 공격자가 사용자를 속여 특정 요청을 수행
- 사용자가 의도하지 않은 요청이 서버로 전송돼서 서버에서 의도하지 않은 동작 발생 가능
- 클라이언트를 보호하는 관점
- 사용자의 민감한 정보(쿠키, 세션 데이터 등)가 악의적인 스크립트에 의해 탈취되는 것을 방지하기 위함
- XSS (Cross-Site Scripting)
- 공격 대상 : 주로 클라이언트 (브라우저에서 실행되는 사용자)
- 공격 방식 : 공격자가 악성 스크립트를 웹 페이지에 삽입 → 사용자가 페이지를 열었을 때 스크립트가 실행
- 이를 통해 사용자의 세션 정보, 쿠키 등을 탈취 가능
1.5 CORS
- CORS : 브라우저에서 다른 origin으로 요청을 허용하는 메커니즘
- CORS 요청을 할 때 클라이언트, 서버가 해야하는 것
- 클라이언트 : 없음, 요청을 실행하기만 하면 됨
- 서버 : 클라이언트의 origin을 허용하고 응답
- 서버는
Access-Control-Allow-Origin
응답 헤더를 전송 - 이 응답 헤더의 값은
*
또는 특정 origin이 될 수 있음 *
: 서버가 모든 origin의 요청을 수락한다는 의미
- CORS 요청에 대한 응답이 왔을 때
- 클라이언트 : 응답 검토
Access-Control-Allow-Origin
에 * 값이 있거나 origin 값이 클라이언트의 origin과 일치하면 브라우저는 응답을 수락함- 그렇지 않으면 응답을 삭제하고 콘솔에 CORS 설정 오류를 표시
- CORS 메커니즘 과정
1. 브라우저(origin X)가 서버(origin Y)로 요청을 보냄
2. 서버가 브라우저의 요청에 응답, 이때 Access-Control-Allow-Origin 응답 헤더를 포함하고 origin X를 해당 헤더의 값으로 넣음
3. 브라우저 : 서버 응답에 Access-Control-Allow-Origin 응답 헤더의 값에 origin X가 있는지 확인
4.1 origin X가 있다면 응답을 수락
4.2 origin X가 없다면 응답을 폐기하고 CORS 오류 메세지 표시
- origin을 알 수 없는 경우,
Access-Control-Allow-Origin
을null
로 설정할 수도 있음 - origin을 알 수 없는 경우 예시 : 웹사이트가 아닌 파일에 접근하는 경우
2. Preflight
- 위 내용은 CORS라는 메커니즘을 사용해서 origin이 다른 경우 요청을 처리하는 방법
- 실제로 CORS 요청을 보내기 전에 브라우저는 preflight 요청을 보냄
- Preflight 요청의 목적 : 서버가 실제 요청을 허용하는지 확인
- 브라우저가 preflight 요청을 보내는 경우 : 아래 조건 중 하나라도 충족하는 경우
- HTTP 메서드가 GET, POST, HEAD가 아닌 경우
- GET, HEAD, POST가 아닌 경우 데이터 수정/삭제와 관련 있을 수 있으므로 보안상 서버의 허가를 확인
- Content-Type 헤더의 값이 아래 3가지 중 하나가 아닌 경우
application/x-www-form-urlencoded
multipart/form-data
text/plain
application/x-www-form-urlencoded
,multipart/form-data
,text/plain
은 일반적으로 안전한 MIME 타입으로 간주됨- 위 타입 외에
application/json
과 같은 Content-Type은 데이터의 구조가 복잡할 수 있으므로 서버가 이를 처리할 준비가 되어 있는지 확인하기 위해 Preflight 요청이 필요 - 요청 헤더에
Accept
,Accept-Language
,Content-Language
외에 다른 요청 헤더가 있는 경우 - ex.
Authorization
와 같은 헤더가 있다면 preflight 요청 필요 Accept
,Accept-Language
,Content-Language
는 일반적으로 안전한 헤더로 간주됨- 그러나
Authorization
과 같은 다른 헤더는 인증 정보나 사용자 데이터와 관련될 수 있어 보안 검증이 필요함 - 따라서 서버가 이러한 헤더를 수락할 준비가 되어 있는지 확인하기 위해 preflight 요청을 보냄
XMLHttpRequest
객체가 업로드 이벤트를 포함하는 경우- 파일 업로드와 같은 작업은 더 많은 리소스를 필요로 하고 보안 위험이 있을 수 있음
- 따라서 서버가 이러한 요청을 허용할 준비가 되어 있는지 확인하기 위해 Preflight 요청을 보냄
- 위 4가지 조건 중 하나라도 해당된다면, 브라우저는 실제 CORS 요청을 보내기 전에 서버에 preflight 요청을 보내서 서버가 해당 요청을 허용하는지 확인
- Preflight 요청 : HTTP 메서드 중 OPTIONS 메서드를 사용
- 서버가 이에 적절한 응답을 보내면, 브라우저는 그때 실제 요청을 보냄
- Preflight 요청은 CORS 요청을 보낼 때만 preflight 요청을 보냄
- Preflight 요청을 통해 서버가 CORS 요청에 대한 응답을 잘 보낼 수 있도록
- Preflight 요청에서 브라우저는 아래와 같은 응답 헤더를 보냄
Origin
: 클라이언트의 originAccess-Control-Request-Method
: 실제 CORS 요청에서 사용할 HTTP 메서드 (하나의 메서드만 지원)Access-Control-Request-Headers
: 단순하지 않은 헤더들
- 서버는 preflight 요청에서 다음을 확인
- 요청의 origin(
Origin
) - CORS 요청의 메서드(
Access-Control-Request-Method
) - CORS 요청에 포함된 추가 헤더들(
Access-Control-Request-Headers
)
- 서버는 이 정보들이 서버의 허용 목록에 있는지 확인함
- 허용 목록에 있다면 서버는 200번대의 status code로 응답
- 이 응답에는 응답 본문은 없음
- preflight 요청에 대한 응답으로, 서버는 다음과 같은 응답 헤더를 보냄
Access-Control-Allow-Origin
: 허용된 출발지 도메인Access-Control-Allow-Methods
: 허용된 HTTP 메서드들Access-Control-Allow-Headers
: 허용된 요청 헤더들- 이는 서버가 브라우저에게 해당 CORS 요청을 허용한다는 의미
- 이후 브라우저는 해당 서버에게 CORS 요청을 보낼 수 있음
- preflight 요청의 예시
OPTIONS /api/posts HTTP/1.1
User-Agent: Chrome
Host: 127.0.0.1:8080
Accept: */*
Origin: http://localhost:3000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: Security-Level, App-Name
- preflight 응답 예시
HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http//localhost:3000
Access-Control-Allow-Methods: GET, DELETE
Access-Control-Allow-Headers: Security-Level, App-Name
- Preflight 요청을 포함한 CORS 요청을 처리하는 과정
1. 브라우저(origin X)가 서버(origin Y)로 요청 P를 보냄
- 요청 P는 HTTP 메서드 M과 헤더 C를 포함
2.1 HTTP 메서드 M이 간단한 메서드 && 헤더 C가 간단한 헤더라면 -> 요청 P를 서버로 보냄 -> 서버 응답(끝)
- 간단한 HTTP 메서드 : GET, POST, HEAD
- 간단한 헤더 : Accept, Accept-Language, Content-Language, Content-Type(Content-Type은 값이 application/x-www-form-urlencoded, multipart/form-data, text/plain 중 하나인 경우)
2.2 HTTP 메서드 M이 간단하지 않거나 | 헤더 C가 간단하지 않다면 -> preflight 요청 F를 보냄
- 요청 F의 헤더
- origin = X
- Access-Control-Request-Method = M
- (헤더 C가 간단하지 않은 경우) Access-Control-Request-Headers = C
3. 서버 : X, M, C가 허용 목록에 있는지 확인
4. 허용 목록에 있다면 -> 요청 F에 대한 preflight 응답을 보냄
- status code : 200 범위
- 응답 헤더 : Access-Control-Allow-Origin, Access-Control-Allow-Methods, Access-Control-Allow-Headers
5. 클라이언트가 preflight 응답을 받았다면, 이제 요청 P를 서버로 보냄 -> 서버 응답(끝)
- Preflight 요청에 대한 응답은 캐시 가능
- 따라서 캐시가 있다면 preflight 요청을 보내지 않아도 됨
- 캐시의 기준 : origin + path
- ex.
https://www.naver.com/api/data
3. Images
- 다른 origin의 이미지는 처리를 제한
- 이미지의
src
속성이 클라이언트와 origin이 다른 경우 - 브라우저는 해당 이미지를 로드, 표시는 가능하지만 조작은 불가
- 조작 예시 :
toBlob
,toDataURL
,getImageData
- 이미지에 대해 CORS를 활성화하는 방법
- 즉, origin이 다른 이미지의 조작을 가능하게 하는 방법
crossOrigin
속성의 값을anonymous
또는user-credentials
로 설정anonymous
: 자격 증명이 필요하지 않은 경우user-credentials
: 자격 증명이 필요한 경우
4. CORS 요청, 응답에 사용되는 헤더
4.1 요청 헤더
Origin
- 브라우저에 의해 추가됨
- 클라이언트의 origin을 표시
- 여기에 대응되는 응답 헤더 :
Access-Control-Allow-Origin
Access-Control-Request-Method
- 브라우저가 preflight 요청 시 추가함
- 실제 CORS 요청에서 클라이언트가 사용할 HTTP 메서드를 포함
- 여기에 대응되는 응답 헤더 :
Access-Control-Allow-Methods
Access-Control-Request-Headers
- 브라우저가 preflight 요청 시 추가함
- 실제 CORS 요청에서 클라이언트가 보낼 HTTP 헤더를 포함
- 여기에 대응되는 응답 헤더 :
Access-Control-Allow-Headers
4.2 응답 헤더
Access-Control-Allow-Origin
- 값이
*
일 경우 모든 출처가 허용됨을 의미 - 특정 값이 있을 경우 서버가 해당 origin에서만 요청을 허용함을 의미
- 값이
null
일 경우 웹 사이트가 아닌 출처(예: 파일)에서의 요청만 허용함을 의미 - 여기에 대응되는 요청 헤더 :
Origin
Access-Control-Allow-Credentials
- 값이
true
이면 서버가 쿠키와 같은 사용자 자격 증명의 사용을 지원함을 나타냄
Access-Control-Allow-Methods
- 서버에서 허용하는 HTTP 메서드들의 목록
- 간단한 메서드(
GET, POST, HEAD
)는 포함할 필요는 없지만, 포함하는 것이 좋은 관행 - 이 헤더는 preflight 요청에 대한 응답으로만 보내야 함
- 여기에 대응되는 요청 헤더 :
Access-Control-Request-Methods
Access-Control-Allow-Headers
- 서버에서 허용하는 HTTP 헤더들의 목록
- 이 헤더는 preflight 요청에 대한 응답으로만 보내야 함
- 여기에 대응되는 요청 헤더 :
Access-Control-Request-Headers
Access-Control-Max-Age
- preflight 요청에 대한 응답의 캐시를 유지할 최대 시간
- 이 헤더는 preflight 요청에 대한 응답으로만 보내야 함
Access-Control-Expose-Headers
- 브라우저가 읽을 수 있는 헤더들
- 선택 사항이며, CORS 요청이 성공적으로 처리되기 위해 반드시 필요하지는 않음
Share article