-
web security ( XSS, CSRF, CORS와 OPTIONS 메서드 )개발 2020. 5. 20. 23:43
HTTP 요청은 기본적으로 Cross-Site HTTP Request가 가능합니다.
즉, <img> 태그로 다른 도메인의 이미지 파일을 가져오거나, <link> 태그로 다른 도메인의 CSS를 가져오거나 <script>태그로 다른 도메인의 JavaScript 라이브러리를 가져오는 것이 가능하다는 말입니다.
하지만 <script></script> 로 둘러 쌓여 있는 스크립트에서 생성된 Cross-Site HTTP Request는 Same Origin Policy를 적용 받기 때문에 Cross-Site HTTP Request가 불가능 합니다.
( Same Origin Policy란 어떤 출처에서 불러온 문서나 스크립트가 다른 출처에서 가져온 리소스와 상호작용하는 것을 제한하는 중요한 보안 방식입니다. )
AJAX가 널리 사용되면서 <script></script> 로 둘러 쌓여 있는 스크립트에서 생성되는 XMLHttpRequest에 대해서도 Cross-Site HTTP Request가 가능해야 한다는 요구가 늘어나자 W3C에서 CORS라는 이름의 권고안이 나오게 된 것입니다.
그렇다면 CORS가 무엇 일까요?
CORS란 Cross-origin Resource Sharing의 줄임말로, HTTP 전반에서 추가 HTTP 헤더를 사용하여 한 출처에서 실행중인 웹 어플리케이션이 다른 출처의 선택한 자원에 접근할 수 있는 권한을 부여하도록 브라우저에게 알려주는 체제입니다.
즉, Cross-Site HTTP Request를 가능하게 하는 표준 규약이라고 할 수 있습니다.
CORS 요청의 종류
CORS 요청은 Simple/Preflight 와 Credential/Non-Credential의 조합으로 4가지가 존재합니다.
브라우저가 요청 내용을 분석하여 4가지 방식 중 해당하는 방식으로 서버에 요청을 날리므로, 개발자가 목적에 맞는 방식을 선택하고 그 조건에 맞게 코드를 작성해 나가면 됩니다.
* Simple Request
Simple Request 는 다음과 같은 3가지 조건을 모두 만족하면 Simple Request 라고 합니다.
- GET, HEAD, POST 중의 한 가지 방식을 사용해야 한다.
- POST 방식일 경우 Content-Type이 아래 셋 중의 하나여야 한다.
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain - 커스텀 헤더를 전송하지 말아야 한다.
Simple Request는 서버에 1번 요청하고 서버도 1번 응답하는 것으로 처리가 종료됩니다.
* Preflight Request
Simple Request 조건에 해당하지 않으면 브라우저는 Prefliget Request 방식으로 요청합니다.
따라서, Preflight Request는 GET, HEAD, POST 외의 다른 방식으로도 요청을 보낼 수 있고, application/xml 처럼 다른 Content-Type으로도 요청을 보낼 수 있습니다. 또한 커스텀 헤더도 사용이 가능합니다.
Preflight Request는 이름처럼 예비 요청과 본 요청으로 나뉘어 전송됩니다.
먼저 서버에 예비 요청( Preflight Request )을 보내고 서버는 예비 요청에 대해 응답하고, 그 다음에 본 요청( Actual Request )을 서버에 보내고, 서버도 본 요청에 응답합니다.
하지만 예비 요청과 본 요청에 대한 서버의 응답을 개발자가 직접 구분하여 처리하는 것은 아닙니다.
개발자가 Access-Control- 계열의 Response Header만 적절히 잘 정해주면 OPTIONS라는 메서드 요청으로 오는 예비 요청과 GET, POST, PUT, DELETE 등으로 오는 본 요청의 처리는 서버가 알아서 처리합니다.
여기서 OPTIONS 라는 메서드가 언급되었는데 OPTIONS는 무엇인지 왜 필요한지 CORS와 무슨 연관이 있어서 언급된건지 궁금할 수 있습니다.
* OPTIONS 메소드?
OPTIONS 란, CORS 에서 OPTIONS 메소드를 통해 Preflight Request 요청, 즉 사전 요청을 보내 서버가 해당 parameters를 포함한 요청을 보내도 되는지에 대한 응답을 줄 수 있게 하는 것이라고 MDN에 정의되어 있습니다.
POST 메서드로 요청을 보내면 POST는 POST 요청만 하지않고 확인하고자 하는 주소의 OPTIONS 메서드를 먼저 요청하고 200번의 응답을 확인해야지만 POST 메서드를 정상적으로 호출할 수 있습니다. 이것이 Preflight Request 라고 하는 것입니다.
왜 POST 요청 전에 OPTIONS 메서드를 먼저 사용해야 하냐면 POST 메서드 같은 경우는 많은 양의 데이터를 전송할 수도 있는 메서드다보니 서버에 확인도 하지 않고 이 많은 양의 데이터를 전송하는 것이 낭비이기 때문에 OPTIONS 메서드를 통해 먼저 서버에서 지원하고 있는 메서드 (GET, PUT, DELETE 등..) 을 확인하기 위함입니다.
다음은 OPTIONS 메서드를 통해 오고 간 요청들을 파이어폭스에서 네트워크 창으로 확인한 결과입니다. 이 결과를 가지고 좀 더 자세히 설명하겠습니다.
OPTIONS 요청을 보낼 때에는 다음과 같은 코드를 작성하였습니다.
if(request.method === 'OPTIONS'){ response.writeHead(200, defaultCorsHeader); response.end(); }; const defaultCorsHeader = { "access-control-allow-origin": "*", "access-control-allow-methods": "GET, POST, PUT, DELETE, OPTIONS", "access-control-allow-headers": "content-type, accept", "access-control-max-age": 10, };
OPTIONS 메서드로 요청이 들어간 결과 네트워크에는 다음과 같은 응답과 요청이 이루어졌습니다.
OPTIONS 요청 시에 defaultCorsHeader의 정보를 응답 헤더에 담아 보내게 됩니다.
이는 클라이언트에게 "이 서버에서 무엇이 허용되고 어떻게 요청을 보내야 하는지" 를 알려주는 것이라고 할 수 있습니다.
응답 헤더에 대해서 간략하게 설명하자면 다음과 같습니다.
- origin 이 "*"이란 소리는 모든 클라이언트에서 요청을 보낼 수 있다는 얘기고
- methods는 'GET, POST, PUT, DELETE, OPTIONS' 가 허용되며
- header에는 'content-type, accept' 정보를 담아 보내야 하고
- max-age는 Preflight Request 의 결과가 캐쉬에 얼마나 오래동안 남아있을 지를 나타내는 것입니다.
요청 헤더에서는 현재 요청하고 있는 정보를 담고 있습니다.
- Access-Control-Request-Method 헤더는 Preflight Request 요청의 일부분으로 서버에게 실제 요청이 전달 될 때 POST 요청 메소드로 전달될 것 임을 명시합니다.
- Access-Control-Request-Headers 헤더는 서버에게 실제 요청이 전달될 때 content-type 으로 전달될 것 임을 명시합니다.
서버는 그럼 이러한 요구사항들에 맞춰 요청을 수락할 것인지 정할 수 있는 것입니다.
정리하기
앞에서 설명했듯이 CORS가 등장하기 전 까지는 보안상의 이유로 다른 출처는 악용의 우려가 있다고 판단하여 차단했기 때문에 한 오리진에만 접근할 수 있도록 설계 되어있었다고 했습니다. ( 위에서 언급한 Same Origin Policy 입니다)
CORS를 적용하게 되면서 다른 악용의 우려가 있는 출처를 막기 위해서 서버에서 허용 범위를 지정하고 ( 위의 코드에서 작성한 defaultCorsHeader 내용 ) 이 서버가 허용한 범위 내에서만 Cross Origin 요청을 허용하기 위해서 확인 차 사용하는 메서드가 바로 OPTIONS 메서드인 것입니다!!!
XSS 란?
XSS 는 클라이언트가 서버를 신뢰해서 벌어지는 보안 이슈입니다.
서버에 요청하고 일방향으로 응답을 받는 점을 악용해 게시판이나 웹 메일 등에 자바스크립트와 같은 스크립트 코드를 삽입하여 개발자가 고려하지 않은 기능이 작동하게 하여 클라이언트를 공격하는 기법입니다.
위의 그림처럼 공격자가 미리 XSS 공격에 취약한 웹 사이트를 탐색하고, XSS 공격을 위한 스크립트를 포함한 URL 등을 사용자에게 노출시킵니다. 이런 사실을 모를 수 밖에 없는 사용자는 해당 URL을 클릭 할 경우, 취약한 웹 사이트의 서버에 스크립트가 포함된 URL을 통해 Request 를 전송하고, 웹 서버에서는 해당 스크립트를 포함한 Response를 전송하게 되는 것입니다.
XSS 를 방지할 수 있는 방법
XSS 공격은 IPS, IDS, 방화벽 등으로도 방지할 수가 없기 때문에 단순히 문자를 필터링 하는 등의 방법만이 존재합니다.
XSS 공격은 입력값에 대한 검증이 제대로 이루어지지 않아 발생하는 취약점 입니다.
때문에 사용자의 모든 입력값에 대하여 주로 스크립트를 실행하기 위한 특수문자를 정규표현식으로 필터링하는 방법입니다.
// 예시) 스크립트 태그의 < 부분을 필터링하고 있습니다. .replace(/</g, '<');
CSRF 란?
CSRF는 XSS와 반대로 서버가 클라이언트를 신뢰해서 벌어지는 보안 이슈입니다.
XSS를 이용한 공격이 사용자가 특정 웹사이트를 신뢰하는 점을 노린 것이라면, CSRF는 특정 웹사이트가 사용자의 웹 브라우저를 신뢰하는 상태를 노린 것입니다.
사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위( 수정, 삭제, 등록 등 )를 특정 웹 사이트에 요청하게 합니다.
공격 과정
- 사용자는 웹 사이트에 로그인하여 정상적인 쿠키를 발급받습니다.
- 공격자는 공격용 링크를 이메일이나 게시판 등의 경로를 통해 이용자에게 전달합니다.
- 공격용 링크에 도착지를 변조합니다. 예를 들면 이메일을 클릭했을 때 정상적인 경우라면
읽고자 하는 이메일이 열릴테지만 공격용 링크는 도착지를 변경했기 때문에 공격자가 전달한 공격용 링크로 이동하게 되는 것입니다. - 사용자가 공격용 페이지를 열면, 브라우저는 공격용 URL을 열게 됩니다.
- 사용자의 승인이나 인지 없이 출발지와 도착지가 등록됨으로써 공격이 완료됩니다.
위의 내용들은 CORS 와 OPTIONS의 연관성과 XSS와 CSRF에 대해 이해하기가 너무 어려워서 구글링과 MDN 문서를 읽어보면서 정리한 글입니다.
잘못된 부분이 있거나 피드백 주실 내용이 있을 경우에는 알려주시면 감사하겠습니다 :-)
'개발' 카테고리의 다른 글
Virtual DOM 과 DOM (0) 2020.05.26 클래스형 컴포넌트와 함수형 컴포넌트 (0) 2020.05.25 http 개체와 서버구축 (bare code) (2) 2020.05.20 JavaScript 비동기 처리를 위한 Promise (0) 2020.05.19 JavaScript 비동기 처리와 콜백 함수 (0) 2020.05.19