Same-Origin Policy 동일 출처 정책
같은 출처(Origin)의 리소스만 공유가 가능하다.
* 출처(Origin)이란?
프로토콜 + 호스트 + 포트의 조합으로 이 중 하나라도 다르면 다른 출처로 받아들여야 한다.
https://www.codestates.com
vshttp://www.codestates.com
https vs http 두 URI의 프로토콜이 다름 ➡️ 다른 출처- 호스트
https://urclass.codestates.com
vshttps://codestates.com
urclass.codestates.com vs codestates.com 두 URI의 호스트가 다름 ➡️ 다른 출처- 포트
http://codestates.com:81
vshttp://codestates.com
http 프로토콜의 기본 포트는 80이므로 후자의 URI는http://codestates.com:80
과 같다. :81 vs :80 두 URI의 포트가 다름 ➡️ 다른 출처https://codestates.com:443
vshttps://codestates.com
두 URI 는 프로토콜 https 와 호스트 codestates.com 과 포트 :443 이 같음 ➡️ 같은 출처
잠재적으로 해로울 수 있는 문서를 분리함으로써 공격받을 수 있는 경로를 줄인다. 다른 사이트와의 리소스 공유를 제한한다. ➡️ 해킹의 위협에서 보다 안전, 보안상 유리
Cross-Origin Resource Sharing 교차 출처 리소스 공유
추가 HTTP 헤더를 사용하여 한 출처에서 실행 중인 웹 애플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 함. SOP 의 다른 리소스 공유 문제 해결.
ㄴ http://localhost:
를 fetch하려고 했지만 다른 출처의 리소스를 공유하지 못하는 SOP 때문에 접근 불가하여 CORS 설정을 통해 서버의 응답 헤더에 Access-Control-Allow-Origin
을 작성하여 접근 권한을 얻을 수 있다.
브라우저는 서버에 실제 요청을 보내기 전에, 프리플라이트 요청(OPTIONS 메서드로 사전 요청을 보내 해당 출처 리소스에 접근 권한이 있는지 확인하는 것)을 보내고, 응답 헤더의 Access-Control-Allow-Origin
으로 요청을 보낸 출처가 돌아오면 실제 요청을 보내게 된다.
preflight 요청에 대한 응답을 보낼때는 200의 StatusCode 와 응답헤더를 같이 보내주고
요청 메서드가 POST이나 PUT 일 경우에는 201의 StatusCode 와 요청 바디부분이 중요하다.
만약, 프리플라이트 요청을 보냈지만 접근 권한이 없다면 브라우저에서 CORS 에러를 띄우고 실제 요청은 전달되지 않는다.
요청 헤더에 인증 정보를 담아 보내는 요청으로 민감한 정봉기 때문에 프론트와 서버 양측의 CORS 설정이 모두 필요하다.
withCredentials:true
추가Access-Control-Allow-Credentials: true
추가 (단, * 와일드 카드로 설정할 경우 에러 발생)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-Origin", "true");
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"});
});
Access-Control-Allow-Origin
보내야 한다.if(request.method === 'OPTIONS'){
response.statusCode(200);
response.setHeader(" " , " ");
//위 코드를 축약하여 writeHead 메서드를 이용해 응답 헤더를 설정할 수 있음
//응답헤더가 이미 작성되어 있기 때문에 writeHead 메서드 사용
response.writeHead(200, defaultCorsHeader);
response.end();//응답 마침
}
if(request.method === 'POST' && request.url === '/upper'){
let body = [];
request.on("data", (chunck) => {
body.push(chunk);
}).on("end", () => {
body = Buffer.concat(body).toString().toUpperCase();
response.writeHead(201, defaultCorsHeader);
response.end(body);
});
}else if(request.method === 'POST' && request.url === '/lower'){
...
}
대문자로 바꾸어주는 작업은 HTTP 트랜잭션 해부 | Node.js 에 잘 나와있는데,
특히나 메서드가 POST 나 PUT 은 요청 바디가 포함되어 있기 때문에 요청 바디의 데이터를 받는 작업이 중요하다.
핸들러에 전달된 request 객체는 ReadableStream 인터페이스를 구현하고 있다. 이 스트림에 이벤트 리스너를 등록하거나 다른 스트림에 파이프로 연결할 수 있다. 스트림의 'data'와 'end' 이벤트에 이벤트 리스너를 등록해서 데이터를 받을 수 있습니다.
response.writeHead(404, defaultCorsHeader);
response.end();
Access-Control-Allow-Origin : http://localhost:5100
으로 설정해놓은 상태에서
당연히 http://localhost:5100
으로 접속하고 요청하면 응답하지만,
http://localhost:5200
으로 접속하면 CORS 설정을 다시하여 접근권한을 부여해라는 에러 메세지가 뜬다.
이로써 CORS와 SOP에 대한 서버 접근 권한을 알아 볼 수 있었다.
서버 부분이 항상 개념이 잡히지 않아서 백엔드 대신 프론트엔드를 선택한 것도 있지만, 이렇게 작게 나마 개념을 알게 되서 다행이라고 생각한다. 그치만 내가 이해한 것이 제대로 이해한 것이 맞나? 라는 의구심이 들기도 하지만, 라이브 세션에서 자세히 들어봐야 또 알 것 같다.