TIL_CORS

박성훈·2022년 8월 11일
0

백엔드

목록 보기
8/13
post-thumbnail

💡 SOP (Same-Origin Policy)

같은 출처의 리소스만 공유하도록하는 정책

SOP은 다른 사이트와 리소스를 공유하는 것을 제한한다.

그렇다면 다른 사이트의 기준은 무엇일까?
같은 사이트로 판단하는 출처에 대한 정의는 다음과 같다.

URI의 Scheme, Host, Port가 같다면 같은 출처라고 판단한다.

🔍 왜 SOP?

우리가 만약, https://www.web1.com:443 (이하 web1)이라는 사이트에 접속해서 로그인을 해놨다고 가정하자.
이때, User의 설정에 따라 자동로그인이 될 수도 있고, 로그인이 된 상태로 다른 작업을 할 수도 있다.

근데, 로그인이 된 상태에서 다른 작업을 하다가 hackersite에 접속을 하게되고, 그 사이트의 악의적인 코드가 실행되면서 web1의 로그인 정보에 접근하려 할 수 있다.

이러한 상황을 방지하기 위해서, 웹브라우저는 SOP을 사용하여 다른 사이트의 리소스에 접근할 수 없도록 하고 있다.


💡 CORS (Cross-Origin Resource Sharing)

추가 HTTP헤더를 사용하여, 한 출처에서 실행 중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에 알려주는 체제

위에서 살펴본 악의적인 상황을 방지하기위해 SOP을 사용하고 있지만, 우리가 개발환경에서 다른 출처의 정보를 사용할 일은 매우 많다.
Open Api (네이버 지도 api), github 정보 등이 이에 해당한다.

이런 상황을 위해 CORS가 존재한다.

브라우저에서는 SOP에 의해 기본적으로 다른 출처의 리소스 공유를 막지만, CORS를 통해 접근 권한을 얻어 사용할 수 있다.

API에 AJAX요청으로 데이터를 받아올려고 하면, 위의 에러를 자주 만나게된다.

이는 CORS 설정이 잘못되어 접근권한이 올바르지 않아 발생하는 에러이다.
즉, 접근권한이 올바르지 않기 때문에 SOP에 의해 에러가 발생하는 것이다.

🔍 CORS의 동작 방식

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


먼저 클라이언트 쪽에서 요청을 보내면, 브라우저에선 먼저 Preflight Request를 보낸다.
Preflight Request는 서버 측에 CORS설정이 되어있는지 확인한다.
이때, 사용하는 메소드는 OPTIONS이다.

만약, CORS설정의 Access-Control-Allow-Origin에 클라이언트의 URI를 통과한다는 설정이 있으면,
브라우저는 다시 클라이언트가 보낸 실제 요청을 Server측에 전달하고, 서버는 전달받는 요청을 처리하여 응답으로 보내준다.

그렇지않고, 허가되지 않은 URI라면 CORS에러를 반환한다.

🖊 단순 요청 (Simple Request)

클라이언트가 보낸 요청이 특정 조건에 부합한다면, Preflight Request를 생략하고 바로 실제요청을 전달한다.

특정조건은 다음과 같다.

  • 메서드는 GET POST HEAD 중 하나
  • 헤더는 Accept, Accept-Language, Content-Language, Content-Type 만 허용
  • Content-Type 헤더는 다음의 값들만 허용
    - application/x-www-form-urlencoded
    - multipart/form-data
    - text/plain

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

요청 헤더에 인증 정보를 담아 보내는 요청이다.
이 때, 요청을 보내는 클라이언트 측에서는 반드시 WithCredentials에 true값을 넣어줘야하고, 서버에서의 응답객체의 Access-Control-Allow-Credentials에 true값을 넣어줘야 한다.

인증정보를 담아 보내는 만큼, 민감한 정보가 포함되어있기 때문에, Access-Control-Allow-Origin : * 으로 설정했을 경우 에러가 발생한다.

🔍 CORS 설정

🖊 서버생성

const http = require('http');

const server = http.createServer((request, response) => {
	console.log('create server');
}))

server.listen(8080, () => {
	console.log('activate server');
})

📌 nodemon

node로 서버를 실행할 경우, 수정할 때마다 서버를 재실행해야하는 불편함이 있다.
이런 불편함을 해소하기 위해 nodemon을 사용한다.

npm install nodemon

그리고 package.json의 script에 아래 코드를 추가한다.

"start": "nodemon server/basic-server.js"

🖊 요청에 따른 응답

const http = require('http');

const server = http.createServer((request, response) => {
	const { method, url } = request;
  	let body = [];
  	request.on('data', (chunk) => {              // 데이터가 있을 때
   		body.push(chunk);
    }).on('end', () => {						 // 데이터가 끝났을 때
    	body = Buffer.concat(body).toString();
    })
  
  	if (method === 'POST' && url === '/lower'){
    	response.end(body.toLowerCase); // 응답객체에 전달
    }
  
}))

server.listen(8080, () => {
	console.log('activate server');
})

request.on는 addEventListener와 유사하게 동작한다.
이를 확실하게 이해하기 위해선 우리가 흔히 사용하고 있는 '스트리밍 서비스'에 대해 이해해야한다.

📌 스트리밍 (Streaming)

스트리밍 방식은 말 그대로, 데이터가 시냇물 처럼 흘러오는 것을 말하는데, 위의 그림처럼 데이터가 흘러오면
서버에서 선언한 배열에 데이터를 순서대로 버퍼 형식으로 할당한다.

그리고, 마지막 데이터를 받게 되면, 지금까지 받은 데이터를 concat 메소드로 합치고 toString으로 우리가 볼 수 있는 형태로 바꿔준다.

지금까지 말한 내용을 코드로 표현한 것이 위에서 본 코드인 것이다.

const server = http.createServer((request, response) => {
	const { method, url } = request;
  	let body = [];
  	request.on('data', (chunk) => {              // 데이터가 있을 때
   		body.push(chunk);
    }).on('end', () => {						 // 데이터가 끝났을 때
    	body = Buffer.concat(body).toString();
    })

🖊 CORS 처리

const http = require('http');

const server = http.createServer((request, response) => {
	const { method, url } = request;
  	let body = [];
  	request.on('data', (chunk) => {              // 데이터가 있을 때
   		body.push(chunk);
    }).on('end', () => {						 // 데이터가 끝났을 때
    	body = Buffer.concat(body).toString();
    })
  
  	
  	if (method === 'OPTIONS'){                           // Preflight Request 처리
    	response.writeHead('200', defaultCorsHeader);    // 서버의 CORS 설정 전달
      	response.end();
    }
  
  	else if (method === 'POST' && url === '/lower'){
    	response.writeHead('200', defaultCorsHeader);
    	response.end(body.toLowerCase);
    }
  
}))

server.listen(8080, () => {
	console.log('activate server');
})

const defaultCorsHeader = {
  'Access-Control-Allow-Origin': '*',    // 모든 출처를 허용한다
//'Access-Control-Allow-Origin': http://localhost:3000  -> 로컬호스트는 허용한다.
  'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
  'Access-Control-Allow-Headers': 'Content-Type, Accept',
  'Access-Control-Max-Age': 10
};
profile
프론트엔드 학습일지

0개의 댓글

관련 채용 정보