Post

[HTTP] 05. 보안 - 14장. CORS

[HTTP] 05. 보안 - 14장. CORS

05. 보안


14장. CORS

14-1. CORS의 동작 원리

  • 용어 정리: 출처, 교차 출처 요청
    • 출처: URL에 프로토콜, 호스트, 포트 세가지를 모아서 출처라고 한다.
      • 이 세가지가 같으면 출처가 같다 라고 표현한다.
      • 이 중에 하나라도 다르면 출처가 다르다 라고 표현한다.
    • 교차 출처 요청: 다른 출처의 자원을 사용하기 위해 네트워크 요청을 만드는걸 말한다.
  • 재현: 웹 서버 준비
  • 재현: 교차 출처 요청
  • 해결: 서버가 허용할 출처를 명시한다.

14-2. 단순 요청

14-3. 사전 요청

  • PUT, PATCH, DELETE 메소드를 사용한다.
    • 이런 경우에 서버는 이 요청을 보낸 측이 브라우저가 아닐 수 있다라고 판단한다.
    • 이 경우에 브라우저와 서버는 서로 한번 더 확인하기 위한 요청을 먼저 주고 받는다. => 사전 요청, preflight request
  • 사전 요청 캐시

14-4. CORS를 사용하는 요청

14-5. 중간 정리

  • CORS의 동작 원리
  • 단순 요청
  • 사전 요청
  • CORS를 사용하는 요청

참고


예제

파일구조

  • /ch14
    • public
      • favicon.ico
      • index.html
      • myfont.otf
      • resource.json
      • script.js
    • shared
      • message.js
      • serve-static.js
    • server.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const init = async () => {
  // // 단순 요청
  // const res = await fetch('http://localhost:3001/resource.json');

  // // custom header 요청 (안전하지 않은 헤더 테스트)
  // const res = await fetch('http://localhost:3001/resource.json', {
  //   headers: {
  //     'X-Foo': 'foo',
  //   },
  // });

  // 사전요청 (PUT, PATCH, DELETE 메소드를 사용)
  const res = await fetch('http://localhost:3001/resource.json', {
    method: 'PUT',
  });

  const data = await res.text();
  const jsonEl = document.createElement('pre');
  jsonEl.textContent = data;
  document.body.appendChild(jsonEl);
};

document.addEventListener('DOMContentLoaded', init);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Message {
  constructor(text) {
    this.text = text;
    this.timestamp = Date.now();
  }

  toString() {
    return JSON.stringify({
      text: this.text,
      timestamp: this.timestamp,
    });
  }
}

module.exports = Message;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
const fs = require('fs');
const path = require('path');

const serveStatic = (root) => {
  return (req, res) => {
    const filepath = path.join(root, req.url === '/' ? '/index.html' : req.url);

    fs.readFile(filepath, (err, data) => {
      if (err) {
        if (err.code === 'ENOENT') {
          res.statusCode = 404;
          res.write('Not Found\n');
          res.end();
          return;
        }

        res.statusCode = 500;
        res.write('Internal Server Error\n');
        res.end();
        return;
      }

      const ext = path.extname(filepath).toLowerCase();
      let contentType = 'text/html';
      switch (ext) {
        case '.html':
          contentType = 'text/html';
          break;
        case '.js':
          contentType = 'text/javascript';
          break;
        case '.css':
          contentType = 'text/css';
          break;
        case '.png':
          contentType = 'image/png';
          break;
        case '.json':
          contentType = 'application/json';
          break;
        case '.otf':
          contentType = 'font/otf';
          break;
        default:
          contentType = 'application/octet-stream';
      }
      res.setHeader('Content-Type', contentType);

      res.write(data);
      res.end();
    });
  };
};

module.exports = serveStatic;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const http = require('http');
const path = require('path');
const static = require('./shared/serve-static');

const handler = (req, res) => {
  res.setHeader('Access-Control-Allow-Origin', 'http://localhost:3000'); // 출처명시: http://localhost:3000 도 사용 허용한다.
  res.setHeader('Access-Control-Allow-Headers', 'X-Foo'); // 커스텀 헤더 이름 명시
  res.setHeader('Access-Control-Allow-Methods', 'PUT'); // 사전요청 (PUT, PATCH, DELETE 메소드를 사용)
  res.setHeader('Access-Control-Max-Age', '10'); // 사전 요청은 캐시로 네트워크 비용을 아낄 수 있다.

  static(path.join(__dirname, 'public'))(req, res);
};

const server = http.createServer(handler);
const port = process.env.PORT || 3000;
server.listen(port, () => console.log(`server is running ::${port}`));

// $ PORT=3001 node ch14/server
This post is licensed under CC BY 4.0 by the author.

Trending Tags