

HTTP 프로토콜의 특성이자 약점을 보완하기 위해 쿠키 또는 세션을 사용한다
기본적으로 HTTP프로토콜 환경은 stateless한 특성을 가지기 때문에 서버는 매 연결이 독립적으로 이루어진다.
하지만 로그인과 같은 경우, 상태 유지가 필수적이기 때문에 서버에 전송할 때 쿠키를 같이 전송하므로써 클라이언트가 누구인지 확인할 수 있다.
JWT은 JSON객체에 인증이 필요한 정보들을 담은 후 비밀키로 서명한 토큰으로, 인터넷 표준 인증 방식이다. 공식적으로 인증(Authentication) & 인가(Authorization) 방식으로 사용된다.

(로그인 전)
1. 사용자가 아이디와 비밀번호 혹은 소셜 로그인을 이용해 서버에 로그인 요청을 보낸다.
2. 서버는 비밀키를 사용해 JSON객체를 암호화한 JWT 토큰을 발급한다.
3. JWT를 헤더에 담아 클라이언트에게 보낸다
(로그인 후)
1. 클라이언트는 JWT를 로컬에 저장해놓는다
2. API 호출을 할 때마다 header에 JWT를 실어 보낸다
3. 서버는 헤더를 매번 확인하여 사용자가 신뢰할만한지 체크하고, 인증이 되면 API에 대한 응답을 보낸다.
JWT는 Header, Payload, signiture 3개로 구성되어있다.

header와 payload는 단순히 Base64로 인코딩되어 있어 누구나 쉽게 복호화할 수 있지만, signiture는 key가 없으면 복호화할 수 없다. 이를 통해 ##보안상 안전하다##는 특성을 가질 수 있게 되었다.
SOP(Same-Origin policy; 동일 출처 정책)은 자바스크립트 엔진 표준의 보안 규칙으로 하나의 출처에서 로드된 자원(문서나 스크립트)이 호스트나 프로토콜, 포트번호가 일치하지 않는 자원과 상호작용하지 못하도록 요청 발생을 제한하고 동일 출처(same origin)에서만 접근이 가능하도록 하는 정책
동일 출처란 두 URL의 프로토콜, 포트, 호스트가 모두 같은 출처이다
예를 들어 http://store.company.com/dir/page.html와 동일 출처인 것은 아래와 같다

아래와 같은 교차 삽입의 경우 SOP에 상관없이 허용된다
<link>태그로 다른 도메인의 CSS 가져오기<script>태그로 다른 도메인의 Javascript 가져오기(단, 특정 ip에서는 차단될 수 있음)<img>,<video>,<audio>,<embed>,<object><iframe>으로 삽입되는 모든 것@font-face교차 읽기(JS로 접근한 경우)는 허용되지 않는다\
IE의 경우에 SOP에 두 가지 중요한 예외사항이 있다
- 신뢰가능한 사이트: 양쪽 도메인 모두가 높음 단계의 보안 수준을 가지고 있는 경우 동일 출처 제약을 적용하지 않는다.
- 포트 무시 : 포트가 다르더라도 동일 출처로 여김
교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS)는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제이다. 웹 애플리케이션은 리소스가 자신의 출처(도메인, 프로토콜, 포트)와 다를 때 교차 출처 HTTP 요청을 실행한다.
쉽게 설명하자면 CORS란 도메인이 다른 서버끼리 리소스를 주고 받는 정책이라고 생각하면 된다. 이게 왜 문제가 되냐면, 기본적으로 웹 브라우저의 기본 정책은 Same-Origin으로, origin이 다른 서버와의 리소스 공유를 허용하고 있지 않기 때문이다.
여기서 origin이란 scheme(일반적으로 프로토콜), host(도메인), port를 모두 포함하는 것으로, 셋 중 하나라도 일치하지 않는다면 다른 origin으로 판단한다. 예를 들어, 한 컴퓨터에서 React 서버(3000 포트)와 Springboot(8080 포트) 서버를 모두 띄워서 서로 리소스를 주고 받으려 한다면 포트가 다르기 때문에 origin이 달라 CORS 위반 문제가 발생하고, 개발자 도구 창에서는 시뻘건 CORS 위반 에러 메시지를 볼 수 있게 된다.

출처를 비교하는 로직은 서버가 아닌 브라우저에 구현되어 있다.
따라서, SOP를 위반하는 리소스 요청을 해도 서버에서는 정상 응답을 한 로그로 남을 수 있다
SOP를 위반하는 경우 브라우저는 요청의 헤더에
Origin,referer를 추가한다
Origin: 요청이 시작된 서버를 나타내는 urlreferer: 현재 요청된 페이지의 링크 이전의 웹페이지 주소
다음과 같은 요청에 CORS가 사용된다
@font-facedrawImage()를 사용해 캔버스에 그린 이미지/비디오 프레임JavaScript, CSS같은 리소스들은 Cross-Origin 정책을 따르기 때문에 외부 요청이 가능하다
기본적으로 브라우저는 HTTP 요청을 보낼 때, 사전에 OPTIONS 메서드를 통한 HTTP 요청을 보내서, 본 요청을 보내기 전에 안전한지 확인한다. 이를 preflight request라고 한다.
이 때 요청하려는 메서드 정보를 Access-Control-Request-Method 헤더에, 요청에 담길 헤더 정보를 Access-Control-Request-Headers 헤더에 담는다.
OPTIONS /resources/post-here/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:71.0) Gecko/20100101 Firefox/71.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Connection: keep-alive
Origin: http://foo.example
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type
그럼 서버는 응답으로 어떤 것들을 허용하는지에 대한 정보를 담아서 돌려준다
HTTP/1.1 204 No Content
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2
Access-Control-Allow-Origin: https://foo.example
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
이런 식으로 Access-Control-Allow-Origin에는 허용된 origin 정보를,
Access-Control-Allow-Methods에는 허용하는 메서드를,
Access-Control-Allow-Header에는 사용 가능한 헤드 목록을,
Access-Control-Max-Age에는 현재의 preflight request를 브라우저가 캐싱 가능한 최대 시간을 담아서 제공한다
하지만 이런 preflight request를 보내지 않는 경우가 있는데, 대표적인 경우 브라우저를 사용하지 않는 경우(ex-postman 사용)가 있는데, 이를 simple request라고 한다. simple request는 다음의 세 가지 조건을 만족해야 한다
GET, HEAD, POST 중 하나일 것CORS-safelisted request header라는 헤더 목록에 들어 있는 헤더 외에 다른 헤더를 수동으로 넣어주지 않았을 것이 경우에는 preflight 요청을 보내지 않고 서버가 본 형태에 대한 응답 헤더에 CORS관련된 헤더를 보내서 브라우저가 이를 검사하는 형태로 CORS 위반 정책 여부를 검사한다
이외에 헤더에 인증과 관련된 정보를 담아서 보내는 credentialed request라는 경우도 있는데, 이 경우에는 credentials 옵션을 사용하며 CORS 정책 위반 여부를 검사하는 규칙에 몇 가지 규칙이 더 들어가게 된다.
자바스크립트의 fetch API를 사용하거나 Axios, Ajax 등을 사용할 때 서버로 쿠키를 함께 전송해야 하는 경우가 있는데, 요청에 쿠키가 담기게 되면 Credentialed Request 허용이 되어 있어야 한다. 이 때는 서버 쪽에서 응답 헤더에 Access-Control-Allow-Credentials: true를 보내주지 않는다면 브라우저에서 응답을 받는 것을 거부하게 된다.
여기서 주의할 점이 있다. Credentialed Request의 경우, Access-Control-Allow-Origin 헤더 값이 와일드카드여서는 안된다. 대신 https://foo.com과 같이 구체적인 origin을 지정해주어야 한다.
WebMvcConfigurer를 상속받은 @Configuration 빈을 만들어 주면 된다
@Configuration
public class WebConfig implements WebMvcConfigurer {
public static final String ALLOWED_METHOD_NAMES = "GET,HEAD,POST,PUT,DELETE,TRACE,OPTIONS,PATCH";
@Override
public void addCorsMappings(final CorsRegistry registry) {
registry.addMapping("/api/**")
.allowedMethods(ALLOWED_METHOD_NAMES.split(","))
.exposedHeaders(HttpHeaders.LOCATION);
}
}
WebMvcConfigurer에는 addCorsMappings라는 메서드가 존재하는데, 이 메서드로 CORS정책을 허용해 줄 url을 지정해 줄 수 있다.
origin을 지정해주지 않는다면 기본 값으로 '*'를 지정해서 모든 origin에 대해 CORS를 허용한다
출처 : https://velog.io/@wogkr1383/SOP%EC%99%80-CORS,
https://velog.io/@ohzzi/CORS-%ED%97%88%EC%9A%A9-%EC%A2%80-%ED%95%B4%EC%A3%BC%EC%84%B8%EC%9A%94