[Web] SOP와 CORS

nana·2023년 3월 13일
0

공부

목록 보기
4/7

SOP (Same-origin policy, 동일 출처 정책)

SOP란?

왜 SOP가 필요한가?

기본적으로 브라우저는 토큰이나 쿠키 등과 같이 사용자의 정보와 관련된 데이터를 저장하는데, 만약 해커가 CSRF, XSS 등의 방법으로 해커가 심어 놓은 스크립트가 실행되어 인증 요청을 보내면, 그로 인해 얻은 개인 정보를 해커의 서버로 보내는 등의 문제가 발생할 수 있다.
이를 막기 위해 등장한 것이 동일 출처 정책(SOP)이다.

CSRF (Cross-Site Request Forgery, 크로스 사이트 요청 위조)

공격자가 이용자가 인증된 상태에서 웹 애플리케이션에 의해 수행되는 비인가된 명령을 실행할 수 있게 하는 공격. 이를 위해서 공격자는 이용자가 웹 애플리케이션에 요청하는 HTTP 요청을 위조하여, 이용자의 권한으로 원하지 않는 동작을 수행하도록 유도한다.

XSS (Cross-Site Scripting, 크로스 사이트 스크립팅)

공격자가 웹 페이지에 악성 스크립트를 삽입하여, 이용자 브라우저에서 실행되게 하는 공격. 이를 통해 공격자는 이용자의 개인 정보를 탈취하거나, 이용자의 브라우저를 제어하여 다른 공격을 수행할 수 있다.

출처 비교와 차단?

출처를 비교하는 로직은 서버에 구현된 스펙이 아니라 브라우저에 구현된 스펙이다.
sop

서버는 리소스 요청에 대한 응답을 제대로 주지만 브라우저가 응답을 분석해서 동일 출처가 아니면 오류를 발생시킨다.(서버가 Access-Control-Allow-Origin 헤더에 허용할 출처를 담지 않고 응답을 전달한 경우)
즉, 응답 데이터는 정상이지만 브라우저 단에서 받을 수 없도록 차단한 것이다.

브라우저가 정책으로 차단을 한다는 말은, 브라우저를 통하지 않고 서버 간에 통신을 할때는 정책이 적용되지 않는다는 말과 같다.

즉, 클라이언트 단 코드에서 API 요청을 하는게 아니라, 서버 단 코드에서 다른 출처의 서버로 API 요청을 하면 CORS 에러로부터 자유로워 진다. 그래서 이를 이용한 프록시(Proxy) 서버라는 것이 있다.


CORS (Cross-Origin resource sharing, 교차 출처 리소스 공유)

CORS란?

  • CORS는 서로 다른 출처 간에도 요청과 응답을 허용하는 정책이다.
  • 시스템 수준에서 타 도메인 간 자원 호출을 승인하거나 차단하는 것을 결정하는 것이다.
  • Origin이 달라도 서버에서 CORS 옵션을 허용해주면 다른 Origin으로 응답을 보낼 수 있다.
  • CORS 이슈는 동일 출처 정책(SOP)에 의거해 다른 출처의 리소스를 사용하거나 상호작용을 하지 못하도록 막고 경고가 발생 하는 것이고, 이를 해결하기 위해 교차 출처 리소스 공유(CORS)를 사용해 접근을 허용하도록 설정하는 것이다.

브라우저의 CORS 기본 동작

  1. 클라이언트는 HTTP Request 헤더에 Origin을 담아서 서버에게 Request 전달
    Origin: http://localhost:3000

  2. 서버는 HTTP Response 헤더에 Access-Control-Allow-Origin을 담아 클라이언트에게 Response 전달
    Access-Control-Allow-Origin: http://localhost:3000

    • 서버가 요청에 대한 응답을 할 때에 헤더에 Access-Control-Allow-Origin이라는 필드를 추가하고, 값으로 '이 리소스를 접근하는 것이 허용된 출처 url'을 전달한다.
  3. 클라이언트에서 Origin과 서버가 보내준 Access-Control-Allow-Origin을 비교한다.

    • 응답을 받은 브라우저는 자신이 보냈던 요청의 Origin과 서버가 보내준 응답의 Access-Control-Allow-Origin을 비교해본 후 차단할지 말지를 결정한다.
    • 만약 유효하지 않다면 그 응답을 사용하지 않고 버린다. (CORS 에러)
    • 위의 경우에는 둘 다 http://localhost:3000 이므로 유효하기 때문에 다른 출처의 리소스를 문제 없이 가져올 수 있다.

CORS 동작 방식

Preflight Request (사전 요청)

주로 사용하는 방식으로, 본 요청을 보내기 전에 브라우저 스스로 이 요청이 안전한지 확인하기 위해 보내는 사전 요청이다. 이 때, OPTIONS 메서드를 사용한다.
preflight-request

  1. JavaScript의 fetch API를 사용해 브라우저에게 리소스를 받아오라는 명령을 내린다.
  2. 브라우저가 서버에 사전 요청을 보낸다.
  3. 서버는 어떤 것들을 허용하는지에 대한 정보를 Response 헤더에 담아서 브라우저에게 응답을 보낸다.
  4. 브라우저는 미리 보낸 사전 요청과 서버의 응답을 비교한 후, 안전하다고 판단되면 같은 엔드포인트로 다시 본 요청을 보낸다.
  5. 서버가 응답하면 브라우저가 최종적으로 리소스를 JavaScript에 넘겨준다.

왜 Preflight Request가 필요한가?

  • 리소스 측면에서 효율적이다.
  • 브라우저는 응답을 받은 후에 CORS 권한 여부를 판단하기 때문에 DELETE, PUT 처럼 서버의 정보를 삭제하거나 수정하는 요청일 경우 사전 요청이 없다면 허가하지 않는 출처여도 서버는 일단 요청을 처리할 것이고, 이미 다 삭제하고 난 뒤에서야 허가되지 않은 출처임을 알 것이다.

Simple Request (단순 요청)

Preflight request와 동작 방식은 같고 사전 요청의 유무만 다르다. 단순 요청은 사전 요청 없이 그냥 바로 서버에 요청을 보낸다. 하지만 아무때나 단순 요청을 할 수 있는 것은 아니다. 특정 조건을 만족하는 경우에만 사전 요청을 생략할 수 있고, 사실상 조건이 까다로워서 충족하기 어렵다.

  • GET, HEAD, POST 요청 중 하나여야 한다.
  • 자동으로 설정되는 헤더 외에, Accept, Accept-Language, Content-Language, Content-Type 헤더의 값만 수동으로 설정할 수 있다.
    • Content-Type 헤더에는 application/x-www-form-urlencoded, multipart/form-data, text/plain 값만 허용된다.

Credentialed Request(인증정보를 포함한 요청)

좀 더 보안을 강화하고 싶을 때 사용하는 방식으로, 요청 헤더에 인증 정보를 담아 보내는 요청이다. 출처가 다를 경우에는 별도의 설정을 하지 않으면 민감한 정보이기 때문에 쿠키를 보낼 수 없다. 이 경우에는 프론트와 서버 양측 모두 CORS 설정이 필요하다.

  • 프론트 측에서는 요청 헤더에 withCredentials : true를 넣어줘야 한다.
  • 서버 측에서는 응답 헤더에 Access-Control-Allow-Credentials : true를 넣어줘야 한다.
  • 서버 측에서 Access-Control-Allow-Origin을 설정할 때, 모든 출처를 허용한다는 뜻의 와일드카드(*)로 설정하면 에러가 발생한다. 인증 정보를 다루는 만큼 출처를 정확하게 설정해주어야 한다.

CORS 이슈 해결 방법

서버에서 HTTP Response 헤더에 Access-Control-allow-origin을 추가

서버에서 모든 클라이언트에 요청에 대한 cross-origin HTTP 요청을 허가하는 Access-Control-Allow-Origin: *를 헤더에 추가할 수 있다.

하지만 이렇게 설정하면 모든 출처에 대한 요청을 허용하는 것이기 때문에 보안에 취약 할 수 있다.(심각한 보안 이슈를 초래할 수 있음) 따라서 가급적이면 출처를 명시하는 것이 좋다.

Proxy Server

프록시 서버는 중간에서 헤더를 추가하거나 요청을 허용/거부하는 역할을 할 수 있다. 따라서 응답을 주고 받을 때 프록시 서버에서 Access-Control-Allow-Origin: * 을 헤더를 담아 응답해 주면 된다.

webpack-dev-server proxy

프론트엔드에서 webpack-dev-server proxy 기능을 사용하면 서버 쪽 코드를 수정하지 않고 해결 할 수 있다.
webpack-proxy

webpack-dev-server의 프록시를 사용하면, 브라우저에서 API를 요청 할 때 백엔드 서버에 직접적으로 요청을 하지 않고 현재 개발 서버의 주소로 요청을 하게 된다. 해당 요청을 받아 그대로 백엔드 서버로 전달, 백엔드 서버에서 응답한 내용을 다시 브라우저쪽으로 반환한다.


참고)
https://velog.io/@jesop/SOP%EC%99%80-CORS
https://yoo11052.tistory.com/139
https://inpa.tistory.com/entry/WEB-%F0%9F%93%9A-CORS-%F0%9F%92%AF-%EC%A0%95%EB%A6%AC-%ED%95%B4%EA%B2%B0-%EB%B0%A9%EB%B2%95-%F0%9F%91%8F
https://velog.io/@mmm2815/WEB-Server-SOP%EC%99%80-CORS

profile
언젠가 개발자

0개의 댓글