SOP & CORS

현채은·2023년 4월 4일
0

CORS에 들어가기 전에..

💡 SOP (Same-Origin Policy)


: Same-Origin Policy의 줄임말로 동일 출처 정책을 뜻한다.

  • '같은 출처의 리소스만 공유가 가능하다'라는 정책
  • 출처(Origin)?
    • 출처는 프로토콜, 호스트, 포트 총 3가지의 조합으로 되어있다.
    • 이 중 하나라도 다르다면 동일한 출처로 보지 않음 !!
      ex ) https://www.codestates.com vs http://www.codestates.com
      ➡️ 두 URI는 프로토콜이 다르기 때문에 동일한 출처가 아니다.
      ex ) https://urclass.codestates.com vs https://codestates.com
      ➡️ 두 URI는 호스트가 다르기 때문에 동일한 출처가 아니다.
      ex ) http://codestates.com:81 vs http://codestates.com
      • http프로토콜의 기본포트 = 80
        ➡️ 따라서 두 URI 또한 포트번호가 다르기 때문에 동일한 출처가 아니다.
        ex ) https://codestates.com:443 vs https://codestates.com
      • https 프로토콜의 기본 포트번호 = 443
        ➡️ 따라서 두 URI는 동일한 출처이다.

🤷🏻‍♀️ 그래서 SOP은 왜 생겨난거야 ?

  • SOP은 잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄여준다.
    ➡️ 해킹 등의 위협에서 안전해질 수 있음 !
  • 그러나 다른 출처의 리소스를 사용하게 될 일은 너무 많음 !
    ➡️ 어떻게 하면 다른 출처의 리소스를 받아올 수 있을까 ?

💡 CORS (Cross-Origin Resource Sharing )


➡️ 다른 출처의 리소스를 받아오기 위해 필요한 것이 바로 "CORS"

  • Cross-Origin Resource Sharing 의 줄임말로 교차 출처 리소스 공유를 뜻한다.

  • 추가 HTTP 헤더를 사용하여, 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여

  • MDN정의

    교차 출처 리소스 공유(Cross-Origin Resource Sharing, CORS) 는 추가 HTTP 헤더를 사용하여, 한 출처에서 실행 중인 웹 애플리케이션다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제입니다.

  • 즉, 브라우저는 SOP에 의해 기본적으로 다른출처의 리소스 공유를 막지만, CORS를 사용하여 접근 권한을 얻을 수 있는 것


    위 에러를 해석해보면

    다른 출처의 리소스를 가져오려 했으나, SOP 때문에 접근이 불가능 하다.
    CORS 설정을 통해 서버의 응답 헤더에 'Access-Control-Allow-Origin'을 작성하면 접근 권한을 얻을 수 있다.

➡️ 위 에러는 CORS 때문이 아닌 "SOP"에 의해 막힌것 ! CORS는 오히려 이 에러를 해결해 줄 수 있는 해결방안임 👍🏻

💡 CORS의 동작방식


CORS의 동작 방식에는 크게 3가지가 있다.

①. 프리플라이트 요청 ( Preflight Request )

➡️ 실제 요청을 보내기 전 , OPTIONS 메서드로 사전요청을 보내 해당 출처 리소스에 접근 권한이 있는지 확인하는 것

  • 브라우저는 서버에 실제 요청을 보내기 전 프리플라이트 요청을 보냄
  • 요청에 대한 응답헤더의 Access-Control_Allow-Origin 으로 요청을 보낸 출처가 돌아오면 실제 요청을 보내게 된다.

🙋🏻‍♀️ 만약에 프리플라이트 요청을 보냈는데 접근 권한이 없다면요 ?

➡️ 브라우저는 실제요청을 서버에 전달하지 않고 CORS 에러를 전달해줌 ! ( 🖥️ : 너 권한 없어 )

Q. 프리플라이트 요청은 왜 필요할까 ?

  • 실제 요청을 보내기 전 미리 권한 Check 가능
    ➡️ 실제 요청을 다짜고짜 보내는 것 보다 리소스 측면에서 GOOD 👍🏻

  • CORS가 대비되지 않은 서버를 보호할 수 있음
    ➡️ 이전에 만들어진 서버들은 SOP 요청만 들어오는 상황을 고려하고 만들어짐
    ➡️ 따라서 다른 출처에서 들어오는 요청에 대한 대비 X

  • 이런 서버에 요청 전송 ➡️ 응답을 보내기 전 우선 요청을 처리하게 된다.

  • 브라우저는 응답을 받은 후에서야 CORS 권한이 없다는 것을 인지..
    ➡️ 에러를 받은 시점은 이미 요청이 수행된 상태
    ( DELETEPUT 처럼 서버의 정보를 수정,삭제하는 요청이라면 아찔..😵‍💫 )

  • 하지만 CORS에 대비가 되어있지 않은 서버라도 프리플라이트 요청을 먼저 보내면 CORS에러를 띄우게 된다 !


②. 단순 요청 (Simple 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 설정 방법


①. Node.js 서버

➡️ Node.js 로 간단한 HTTP 서버를 만들 경우, 다음과 같이 응답헤더 설정

const http = require('http');

const server = http.createServer((request,response)) => {
  // 모든 도메인 권한부여
  response.setHeader("Access-Control-Allow-Origin", "*"); 
  // 특정 도메인만 권한부여
  response.setHeader("Access-Control-Allow-Origin", "https://codestates.com"); 
  // 인증정보를 포함한 모든 요청을 받을 경우 권한 부여
  response.setHeader("Access-Control-Allow-Credentials","true"); 

②. Express 서버

➡️ Express 프레임워크를 사용해서 서버를 만드는 경우, cors 미들웨어를 사용하여 더욱 간단하게 CORS 설정 가능

const cors = require('cors');
const app = express();

// 모든 도메인 권한부여
app.use(cors());

// 특정 도메인 권한부여
const options = {
  origin : "https://codestates.com", // 접근 권한을 부여하는 도메인
  credentials : true, // 응답헤더에 Access-Control-Allow-Credentials 추가
  optionsSuccessStatus: 200, // 응답상태 200으로 설정
};

app.use(cors(options));

// 특정 요청
app.get("/example/:id", cors(), function(req, res, next) {
  res.json({msg : "example"});
});
profile
프론트엔드 개발자

0개의 댓글