Blog for QTI developers

2016/08/25

JavaScrip와 HTTP 접근 제어

동일 출처 정책에 대하여

  웹 개발을 하다보면 동일 출처 정책[1]을 접하게 된다.
더 정확히 말해서 JavaScript(이하 'JS'로 표기)로 웹 개발을 하려다 보니 동일 출처 정책 문제를 접하게 되었다.
Curl[2]을 사용한다면 알 필요가 없는 부분이긴 하지만 JS를 사용해서 개발하려다 보니 모르고 있을 수 없게 된 것이다.
이 문제는 'AJAX 그리고, JavaScript를 이용한 Google Chart 생성' 포스팅에서 XMLHttpRequest(이하 'XHR'로 표기)를 사용한 예제를 작성하던 중 처음 접하게 되었다.

로컬의 7777 포트에서 XHR을 사용해서 또 다른 로컬의 8000 포트로 request를 했더니, Chrome의 console에 다음과 같은 메세지가 출력되며 request가 실패했다.
XMLHttpRequest cannot load http://127.0.0.1:8000/.
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:7777' is therefore not allowed access.
이처럼 한 자원이 자신이 호스팅되는 출처(Origin Server)와 다른 출처의 자원을 요청할 때 Cross-origin HTTP request가 발생하며, 이것은 동일 출처 정책에 위반된다.
동일 출처란 프로토콜, 호스트명, 포트가 같다는 것을 의미하는데, XHR가 실행한 request의 대상이 되는 포트가 달랐기 때문에 제한된 것이다.

웹 브라우저[3]들은 일반적으로 동일 출처 정책을 따르며, 보안 문제 때문에 스크립트에서 발생시키는 Cross-origin HTTP request는 제한한다.
초기 웹사이트의 보안을 위해서는 동일 출처 정책이 유효했을지 모르지만, 최근과 같이 여러 도메인에 걸쳐 구성되는 웹 프로젝트들을 고려한다면 이 정책은 불편할 수밖에 없다.
다만 위에서 출력된 메세지와 같이 별도의 조치를 취해주면 제한을 피할 수 있다.


CORS에 대하여

  CORS[4] 표준은 서버에세 웝브라우저가 정보를 읽어 들일 수 있는 일련의 출처 정보를 기술하게 하는 새로운 HTTP 헤더를 추가함으로써 Cross-origin 자원 공유에서 발생할 수 있는 문제를 해결한다.
결론부터 말하면, 서버단의 HTTP Response와 클라이언트단의 HTTP Request 각각에 특정 헤더들을 추가했다.

다음은 서버단에 추가된 헤더들에 대한 코드이다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// server.js
    ... 전략 ...
 
function handleRequest(req, res) {
    res.writeHead(200, {
        'Content-Type''application/json',
 
        // 요청을 허용하는 출처.
        'Access-Control-Allow-Origin''*',
        // 클라이언트에서 preflight의 요청 결과를 저장할 기간.
        'Access-Control-Max-Age'3600,
        // 요청을 허용하는 메소드들. 기본값은 GET, POST
        'Access-Control-Allow-Methods''POST, GET, OPTIONS, DELETE',
        // 요청을 허용하는 헤더.
        'Access-Control-Allow-Headers''x-requested-with'
    });
 
    ... 일부 생략 ...
}

다음은 클라이언트단에 추가된 헤더들에 대한 코드이다.
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
56
57
58
59
// server.js
    ... 전략 ...
 
class AjaxRequest {
    constructor() {
    ... 생략 ...
    }
 
    _initXMLHttpRequest() {
    ... 생략 ...
    }
 
    _initAjaxData(func, reqUrl) {
        let xhr = this._initXMLHttpRequest();
        if(xhr != 'undefined') {
            // Event 처리; request 이후 state 변경에 따른 처리를 담당한다.
            let stateHandler = function() {
 
    ... 일부 생략 ...
 
                if(xhr.readyState == 4) { //complete
                    if (xhr.status == 200) {
                        let jsonObj = JSON.parse(xhr.responseText);
 
                        let rs = jsonObj.RecordSet;
                        let rfs = new RecordFields(rs.RecordFields);
                        let rds = new RecordData(rs.RecordData);
 
                        func(rfs, rds);
                    } else if(xhr.status == 404) {
                        console.log('Not found');
                    }
                }
            };
 
            // Event 처리; Time out이 선언되면 처리를 담당한다.
            let timeoutHandler = function() {
                xhr.abort();
                alert('Time Out');
            };
 
            xhr.onreadystatechange = stateHandler; // 상태 변경
            xhr.ontimeout = timeoutHandler; // 응답시간 초과
            // xhr.withCredentials = true;
 
            xhr.open('GET', reqUrl, true);
            // 요청을 보내는 페이지의 출처(도메인).
            xhr.setRequestHeader('Origin''127.0.0.1:7777');
            // 실제 요청하는 메소드.
            xhr.setRequestHeader('Access-Control-Request-Method''GET')
            xhr.send(null);
        } else {
            console.log('AJAX (XMLHTTP) not supported.');
        }
    }
 
    ... 생략 ...
 
//End-of Class


 
-- 각주
  • [1] Same-origin Policy
  • [2] 노파심에서 이야기하면, Curl은 cURL이 아니다.
    Curl에 대해서는 'QTI 홈페이지(http://www.qtii.co.kr)' 또는 'Curl 홈페이지(http://www.curl.com)'을 참조하기 바람.
  • [3] IE에서는 가능하다.
  • [4] CORS; Cross-Origin Resource Sharing
Share:

0 개의 댓글:

댓글 쓰기