HTTPS(Secured HTTP)
HTTPS는요청을 보내고, 응답을 받는 두 주체만 HTTP 요청 및 응답을 읽을 수 있도록 암호화 하는 것이다.
때문에 MITM(Man-In-The-Middle) 공격 방지가 가능하다. 단일 서버와 다수 클라이언트(웹 브라우저)에서, 단일 서버만 사용 가능한 키는 비공개키, 다수 클라이언트 모두 사용 가능한 키를 공개키라 칭한다.
만약 단일 서버의 비공개키가 빼앗긴다면 해당 서버인 것처럼 모든 요청을 받을 수 있다. 단일 서버의 비공개키 진짜 여부 인증을 위해 CA(인증 기관)를 통해 한 번 더 암호화를 거친다. 웹 브라우저도 마찬가지.
* CA: SSL 인증서를 발급하는 기관, 신뢰할 수 있는 기관
대칭키 방식
데이터의 암호화, 복호화 둘 다 동일한 키가 사용되는 방식. 비대칭키(공개키) 방식에 비해 속도과 훨씬 빠르다. 하지만, 대칭키 방식을 사용하기 위해 대칭키를 전송하는 과정이 필요하다. 이 과정에서 대칭키가 제 3자에게 탈취당할 위험성이 존재한다.
공개키(비대칭키) 방식
서로 다른 키로 암호화와 복호화를 수행하는 암호화 기법이다. 예를 들어, A키로 암호화한 데이터는 B키로만 복호화, B키로 암호화한 데이터는 A키로만 복호화가 가능한 것. 웹 서버는 두개의 키 중 한개를 비밀로 보관하고 나머지 한 개를 대중에 공개한다.
CA(Certificate Authority)
HTTP는 통신 상대가 누구인지 확인하지 않는다. 클라이언트가 피싱사이트로 접속한 가짜일수도 있다. 클라이언트는 서버로부터 받은 공개키가 진짜인지 어떻게 판별할 수 있을까? 바로 CA를 통해 가능하다.
첫번째로 서버에서 해당 사이트(velog라든가...)의 정보와 서버 공개키를 함께 CA로 전달한다. CA에서는 이를 검증 후에 인증기관의 개인키로 암호화(서명)한다. 그 후 다시 서버에게 그 인증서를 전달해준다.
CA는 웹 브라우저에게도 인증기관의 공개키를 제공하고, 이는 사용자 브라우저에 자동으로 내장된다.
최종적으로 웹 브라우저에는 인증기관의 공개키가, 웹 서버에는 사이트 인증서와 개인키를 가지게 된다.
그리고 나서 클라이언트가 서버에게 접속을 요청하면, 서버는 CA에서 받은 사이트 인증서를 클라이언트에게 넘겨준다.
클라이언트는 이전에 CA에게서 받은 내장 공개키를 이용해서 해당 인증서를 복호화한다.
그러면 제일 처음 서버가 가지고 있던 사이트의 정보와 서버 공개키를 최종적으로 가지게 된다.
그 후, 클라이언트에서 임의적으로 대칭키를 하나 생성한다. 그것을 복호화 한 서버 공개키로 암호화한다.
그렇게 생성한 사용자 대칭키를 서버에게 다시 보낸다. 그러면 서버가 가지고 있던 개인키를 사용해 대칭키를 복호화하고, 그것을 이용해서 뿅뿅 통신을 하게 된다.
아무리 같은 곳에서 탄생한 개인키와 공개키라도 서로 값이 다른데 어떻게 복호화가 가능한가 궁금했는데, 애초에 처음부터 키를 생성할 때 수학적인 연관성을 가지도록 설계한다고 한다. 이 내용은 다룰 필요가 없으므로 넘어가겠다.
다시 정리하자면 결론적으로 서버와 클라이언트 두 사람만 통신할 수 있도록 키를 가지고 통신하는 것이다. MITM 공격을 방지 할 수 있다. HTTPS는 대칭키 암호화 방식과 비대칭키 암호화 방식의 장점만을 사용하는데, 마지막에 최종적으로 복호화 한 대칭키로 클라이언트와 서버가 동일한 키를 사용해 암호화/복호화 하는 것을 알 수 있다.(서버의 공개키. 연산 속도 무지 빨라요!)
그 다음 1개의 쌍으로 구성된 공개키와 개인키를 암호화/복호화 하는데 사용하기도 했다. 공개키는 누구나 볼 수 있고 키가 노출되어도 비교적 안전하지만 연산 속도가 느리다.
CSRF(XSRF, Cross-Site Request Forgery)
CSRF에 대해서 지난 쿠키/세션 포스팅에 언급했지만, 좀 더 자세하게 살펴보겠다.
웹 브라우저에서 CSRF의 형태는, 유저가 의도하지 않은 요청이 자바스크립트 실행을 통해 서버에 전송되는 것이다. 이때 서버에 요청을 보내는 자바스크립트는 AJAX다.
그렇다면, AJAX를 통한 CSRF를 대비할 수 있는 웹 브라우저의 정책은 무엇일까.
웹 브라우저 비동기 지원 AJAX
FORM(Synchronous)
한번 전송하고 끝난다. HTML 페이지 반환이 이뤄지며, 페이지 리렌더링이 일어난다.
AJAX(Asynchronous JavaScript and XML)
XML 객체를 반환하며 페이지의 리렌더링이 일어나지 않는다. XHR(XmlHttpRequest) 객체를 활용 시에 서버에 데이터를 요청하거나 데이터를 전송 받을 수 있다. 즉, 웹 페이지 전체를 다시 로딩하지 않고 일부분만을 갱신할 수 있게 된다.
웹 브라우저 보안 정책 SOP(Same-Origin Policy)
웹 브라우저에서 HTTP Resouce를 갖고 오기 위한 모든 HTTP 요청은 기본적으로 SOP에 따른다.
Same Origin?
https://www.hello.com:8080
Scheme = https://
Host = www.hello.com
Port = 8080
하지만 웹 브라우저는 이미지, 아이콘처럼 외부에서 정보를 가져오는 경우도 존재하기 때문에 SOP 정책은 'Same Origin'이름과 달리 부분적으로 Cross Origin도 허용한다.
Cross Origin
1. 가져오기 - <img/>, <script/>와 같이 서버 상태를 변경할 수 없는 의도된 조회
2. 제출하기 - 개발자가 설계한 의도된 제출, 의도된 서버 상태 변경
3. 요청하기 - AJAX, POST와 같이 서버 상태 변경 가능한 요청은 SOP에서 불허한다. Cross Origin에 대한 AJAX는 Cross Origin 서버 상태를 바꿀 수 있는 보안 위험성이 존재함.
SOP의 보완 정책인 CORS 등장
앞서 Cross Origin의 '요청하기'는 SOP에서 서버 상태를 바꿀 수 있는 보안 위험성 때문에 불허한다고 했다. 하지만 AJAX는 표준 기술이 아니지만 표준 기술처럼 사용하기 때문에 AJAX를 막게되면 모든 API를 호출할 수 없다. 그래서 결국 SOP를 보완해서 AJAX를 부분적으로 허용할만한 추가 정책이 필요해졌다. 정리하자면 아래 2가지와 같다.
1. AJAX는 CORS 정책에 맞을시에만 조건부 허용하자.
2. 브라우저에서 처리하는 것이다.(서버와 서버 통신에는 적용되지 않는다!)
쉽게 말해서 CORS는 다른 출처에 리소스를 요청할 때 지켜야하는 정책이다. 여기서 출처라 함은 Protocol, Host, Port까지를 의미한다. 이렇게 Cross-Domain 환경에서는 CORS 정책으로 인증이나 쿠키같은 민감한 정보의 교환이 까다롭다.
이렇게 불편한데 왜 필요하죠?
만약 CORS 정책 없이 자유롭게 다른 Origin으로 요청을 보낼 수 있다고 생각해본다면 악의적으로 정보를 빼내는 코드를 담은 요청을 API에 보내서 로그인 정보를 가져온다던지, 무한 요청을 보내서 웹 서버를 터뜨리는 일도 가능하게 될 것이다. 그러니까 허용되지 않은 출처에서 보내는 요청을 브라우저 단에서 막는 것이다.
CORS 서버 측 적용 방법
서버는 브라우저로부터 어떤 요청만 받을지 3가지 CORS 헤어 설정으로 호출가능 요청을 제약한다.
Simple Request - 서버 상태 조회
말 그대로 단순히 요청을 보내는 방법으로, 특정 조건하에서 예비 요청 없이 요청을 보내도 되겠다는 판단하에 예비 요청 없이 서버에 바로 요청을 보내는 방법이다.
서버에 바로 본 요청을 보낸 뒤, 서버는 헤더에 Access-Control-Allow-Origin 값 등을 붙여 보내면 브라우저가 CORS 정책 위반 여부를 검사한다.요청은 GET, HEAD, POST(일부) 중 하나여야 한다.
브라우저는 서버에 변형을 일으키지 않는 일반적인 요청에 대해서는 CORS 정책 검사를 하지 않는다.
브라우저가 서버에게 원 요청을 보내면 서버는 '결과'와 함께 'CORS 헤더'를 같이 보내준다. 브라우저는 'CORS 헤더'가 요청과 부합하지 않으면 반환 결과를 폐기한다.
Preflight Request - 서버 상태 변경
POST, PATCH, PUT과 같은 서버 상태를 변경하는 요청에 대해서는 미리 한 번 확인한다.
CORS 헤더
Acesss-Control-Allow-Origin - 응답을 공유할 수 있는지 여부를 나타냄
Access-Control-Allow-Headers - 요청을 할 때 사용할 수 있는 HTTP 헤더를 나타내기 위해 실행 전 요청에 대한 응답으로 사용
Access-Control-Allow-Methods - 실행 전 요청에 대한 응답으로 리소스에 접근할 때 허용되는 메서드 지정
Access-Control-Max-Age - 실행전 요청 결과를 캐시할 수 있는 기간
Access-Control-Request-Headers - HTTP 헤더를 서버에 알리기 위해 preflight 요청 발행 때 사용
Access-Control-Request-Method - 어떤 HTTP method가 사용될 것인지 서버에 알리기 위해 preflight 때 사용
그 외 몇가지 더 있지만.. 우선적으로 이것들만 알아본다.
왜 미리 확인할까?
만약에 크기가 10만인 데이터를 body에 담아서 보내는 요청이 있다고 가정한다. 이 요청이 서버에서 승인될 수 있을 지는 모르지만 일단 요청을 보낸다. 하지만 뜯어보니 CORS 정책을 위반하는 요청이었고, 이미 데이터는 보내졌다. 만약 이런 요청이 수백만가지가 된다면 불필요한 리소스 낭비 및 서버 부하 발생 우려가 생기는 것이다.
그래서! 이를 방지하기 위해 특정 조건인 경우 예비 요청을 먼저 날리고 가능한지 확인하는 것이다.
OPTIONS /resource/foo
Access-Control-Request-Method: DELETE
Access-Control-Request-Headers: origin, x-requested-with
Origin: https://foo.bar.org
클라이언트가 DELETE 요청을 하기 전에 사전 요청을 통해 묻는다. 만약 서버가 허용한다면 아래와 같이 응답한다.
HTTP/1.1 204 No Content
Connection: keep-alive
Access-Control-Allow-Origin: https://foo.bar.org
Access-Control-Allow-Methods: POST, GET, OPTIONS, DELETE
Access-Control-Max-Age: 86400
Access-Control-Max-Age 헤더를 사용해 동일한 URL을 사용하는 요청에 대해 사전 응답을 선택적으로 캐시할 수 있다.
CORS는 모든 CSRF에 대해 만능 방어다?
1. CORS는 웹 브라우저에서만의 정책이다.
즉, 웹 브라우저와 통신이 아니면 적용되지 않는다. 네이티브 어플리케이션의 경우 CORS가 적용되지 않기에 방어되지 않는다.
2. CORS는 AJAX에 대한 정책이다.
AJAX가 아닌 FORM을 통한 POST 요청에는 CROS 정책이 적용되지 않는다.
'SK Planet ASAC' 카테고리의 다른 글
[SK플래닛 웹 풀스택 ASAC] 2달 다 되어서 쓰는 1개월 회고 (0) | 2024.05.19 |
---|---|
Docker (1) | 2024.05.08 |
Cookie, Session 그리고 Storage (1) | 2024.04.30 |
웹 구성 간 흐름 (1) | 2024.04.28 |
웹과 인터넷, DNS (0) | 2024.04.24 |