본문 바로가기

인증,보안

[인증/보안] 기초2

 

 

  • 학습 내용
  • Cookie

쿠키는 서버에서 클라이언트에 데이터를 저장하는 방법의 하나이다. 서버가 원할 때 서버는 클라이언트에서 쿠키를 이용하여 데이터를 가져올 수 있다. 쿠키를 이용하는 것은 단순히 서버에서 클라이언트에 쿠키를 전송하는 것만 의미하는 것은 아니고 클라이언트에서 서버로 쿠키를 전송하는 것도 포함된다. 서버가 쿠키를 이용해서 데이터를 저장하고 원할 때 이 데이터를 다시 불러와 사용할 수 있지만 데이터를 저장한 이후 아무 때나 데이터를 가져올 수는 없다. 데이터를 저장한 이후 특정 조건들이 만족하는 경우에만 다시 가져올 수 있다.

1) Domain

쿠키 옵션에서 도메인 정보가 존재하면 클라이언트에서 쿠키의 도메인 옵션과 서버의 도메인이 일치해야 한다. 여기서 도메인이란 우리가 흔하게 볼 수 있는 www.gogle.com 과 같은 서버에 접속할 수 있는 이름이다. 쿠키 옵션에 도메인은 포트 및 서브 도메인 정보, 세부 경로는 포함하지 않는다. 서브 도메인이란 위의 도메인의 www 같은 도메인 앞에 추가로 작성되는 부분을 말한다.

2) Path

세부 경로는 서버가 라우팅 할 때 사용하는 경로이다. Path옵션의 특징은 설정된 path를 전부 만족하는 경우 요청하는 Path가 추가로 더 존재하더라도 쿠키를 서버에 전송할 수 있다. 즉 Path가 /users로 설정되어 있고 요청하는 세부 경로가 /users/login 인 경우라도 쿠키 전송이 가능하다. 그러나 /user/login 인 경우에는 Path 옵션을 만족하지 못하기 때문에 서버로 쿠키를 전송할 수 있다.

3) MaxAge or Expires

쿠키가 유효한 기간을 정하는 옵션이다. MaxAge는 앞으로 몇 초 동안 쿠키가 유효한지 설정하는 옵션이다. Expires는 MaxAge와 비슷하다. 다만 언제까지 유효한지 Date를 지정한다. 클라이언트의 시간을 기준으로 한다. 만약 두 옵션이 모두 지정되지 않는 경우에는 브라우저의 탭을 닫아야만 쿠키가 제거될 수 있다.

4) Secure

쿠키를 전송해야 할 때 사용하는 프로토콜에 따른 쿠키 전송 여부를 결정한다. 해당 옵션이 true로 설정된 경우 'HTTPS' 프로토콜을 이용하여 통신하는 경우에만 쿠키를 전송할 수 있다.

5) HttpOnly

자바스크립트에서 브라우저의 쿠키에 접근 여부를 결정한다. 만약 해당 옵션이 true로 설정된 경우 자바스크립트에서는 쿠키에 접근이 불가능하다. 명시되어 있지 않은 경우는 기본으로 false로 지정되어 있는 것이다. 이 옵션이 false인 경우 자바스크립트에서 쿠키에 접근이 가능하므로 'XSS' 공격에 취약하다.

6) SameSite

Cross-Origin 요청을 받은 경우 요청에서 사용한 메소드와 해당 옵션의 조합으로 서버의 쿠키 전송 여부를 결정하게 된다.

Lax Cross-Origin 요청이면 'GET' 메소드에 대해서만 쿠키를 전송할 수 있다.
Strict Cross-Origin이 아닌 same-site인 경우에만 쿠키를 전송 할 수 있다.
None 항상 쿠키를 보내줄 수 있다. 다만 쿠키 옵션 중 Secure 옵션이 필요하다.

여기서 'same-site'는 요청을 보낸 Origin과 서버의 도메인이 같은 경우를 말한다. 이러한 옵션들을 지정한 다음 서버에서 클라이언트로 쿠키를 처음 전송하게 된다면 헤더에 Set-Cookie라는 프로퍼티에 쿠키를 담아 쿠키를 전송하게 된다. 이후 클라이언트 혹은 서버에서 쿠키를 전송해야 한다면 클라이언트는 헤더에 Cookie는 프로퍼티에 쿠키를 담아 서버에 쿠키를 전송하게 된다. 이러한 쿠키의 특성을 이용하여 서버는 클라이언트에 인증정보를 담은 쿠키를 전송하고 클라이언트는 전달받은 쿠키를 요청과 같이 전송하여 Stateless 한 인터넷 연결을 Stateful 하게 유지할 수 있다. 하지만 기본적으로는 쿠키는 오랜 시간 동안 유지될 수 있고 자바스크립트를 이용해서 쿠키에 접근할 수 있기 때문에 쿠키에 민감한 정보를 담는 것은 위험하다.

 

  • Session

Session이란 서버와 클라이언트 연결이 활성화된 상태를 말하는데, 클라이언트에 정보를 저장하는 Cookie와 달리 세션은 데이터를 서버에 저장하고 cookie에는 그 데이터에 대한 암호화된 ID만 부여한다. 그렇게 부여된 session 아이디는 신분증과 같은 역할을 한다. 그래서 그 후에는 session 아이디로만 인증을 거쳐 데이터의 응답을 처리한다. 만약 쿠키에 세션 아이디 정보가 없는 경우에 서버는 해당 요청이 인증되지 않았음을 알려준다.

 세션으로 로그아웃을 구현할 때는 서버의 세션 정보를 삭제하고 클라이언트의 쿠키를 갱신해주면 된다. set-cookie로 세션 아이디의 키 값을 무효한 값으로 갱신할 수 있다. 이런 세션을 대신 관리해 주는 'express-session'이라는 모듈이 존재한다. express-session은 세션을 위한 미들웨어로 Express에서 세션을 다룰 수 있는 공간을 보다 쉽게 만들어 준다. 또한 필요한 경우 세션 아이디를 쿠키에 저장하고 해당 세션 아이디에 종속되는 고유한 세션 객체를 서버 메모리에 저장한다. 이때 세션 객체는 서로 독립적인 객체이므로 각각 다른 데이터를 저장할 수 있다. req.session이 세션 객체이며 req.session은 세션 객체에 세션 데이터를 저장하거나 불러오기 위해 사용한다. 자세한 내용은 https://github.com/expressjs/session#reqsession 문서를 참고하여 익히는 것이 좋다.

 세션과 쿠키의 차이점과 장단점

  설명 접속 상태 저장 경로 장점 단점
Cookie 쿠키는 그저 http의 stateless한 것을 보완해주는 도구 클라이언트 서버에 부담을 덜어줌 쿠키 그 자체는 인증이 아님
Session 접속 상태를 서버가 가짐(stateful) 접속 상태와 권한 부여를 위해 세션아이디를 쿠키로 전송 서버 신뢰할 수 있는 유저인지 서버에서 추가로 확인 가능 하나의 서버에서만 접속 상태를 가지므로 분산에 불리

추가적으로 또 Session의 단점으로 서버의 메모리에 session을 저장하기 때문에 일정 메모리의 공간을 항상 차지하여 가용 메모리의 양이 비교적 부족하고, Cookie를 활용하기 때문에 여전히 XSS 공격에 아주 취약하다는 단점이 있다.

 

  • CSRF

온라인에서는 엄청나게 많은 데이터가 다뤄진다. 만약 웹 앱이 안전하지 않다면 누군가 개인 유저의 정보를 악용할 수 있다. 그래서 개발자들이 웹사이트, 모바일, 웹 API 등을 만들 때에는 해커들의 공격을 막기 위해서 보안(security)은 필수 사항이다. 이 것을 Web Application Security라고 한다. 대표적인 공격 중 SQL injection, XSS, CSRF 등이 있다. 이 중에서 CSRF(Cross Site Request Forgery)란 다른 사이트(cross-site)에서 유저가 보내는 요청(request)을 조작(forgery)하는 것이다. 예를 들어 이메일에 첨부된 링크를 누르면 내 은행계좌의 돈이 빠져나가는 것이다. 해커가 직접 데이터를 접근할 수는 없다. 다른 사이트이기 때문에 response에는 직접 접근할 수 없는 것이다.

 CSRF가 작동하기 위해서 몇 가지 필요한 조건이 있다. 먼저 쿠키를 사용한 로그인이어야 한다. 유저가 로그인했을 때 쿠키로 어떤 유저인지 알 수 있어야 한다. 또 예측할 수 있는 요청/parameter를 가지고 있어야 한다. request에 해커가 모를 수 있는 정보가 담겨있으면 안 된다. CSRF를 막기 위해서 먼저 CSRF 토큰을 사용하는 방법이 있다. 서버 측에서 CSRF 공격에 보호하기 위한 토큰 문자열을 유저의 브라우저와 웹 앱에만 제공하는 것이다. 이 조합으로 생성된 요청만 성공적으로 요청을 완료해 주는 것이다. 두 번째로 same-site cookie를 사용하는 것이다. 같은 도메인에서만 세션/쿠키를 사용할 수 있도록 만들어서 같은 도메인에서 온 요청이 아니면 받아주지 않는 것이다.

 

  • 토큰 기반 인증

세션 기반 인증은 서버 또는 DB에 유저 정보를 담는 방법이다. 그러다 보니 서버에서 매번 요청을 할 때마다 DB를 살펴봐야 한다. 당연히 그로 인한 부담이 크기 때문에 이를 줄위기 위해 토큰을 사용하는 토큰 기반 인증을 많이 쓴다. 대표적인 토큰 기반 인증에 JWT(JSON Web Token)이 있다. 토큰이랑 보통 대가를 지불하고 그에 대한 서비스를 이용할 때 그 증거로 사용한다. 클라이언트에서 인증 정보를 보관하는 방법으로 토큰 기반 인증이 고안되었다. 클라이언트가 토큰을 가지고 있다면 보통의 다른 유저들과 다르게 서버에서 제공하는 다양한 더 프리미엄한 기능을 요청할 수 있는 것이다. 여기서 한 가지 의문이 드는 것은 클라이언트에 데이터를 저장했을 때 XSS, CSRF와 같은 공격 때문에 세션을 사용해서 서버나 DB에 데이터를 저장하도록 했었는데 이게 안전한가라는 의문이 들 수 있다. 그러나 토큰은 유저 정보를 암호화한 상태로 담을 수 있고 암호화했기 때문에 클라이언트에 담아도 안전하다.

 JWT에는 Access Token과 Refresh Token이 있다. Access Token은 보호된 정보들(유저의 이메일, 연락처, 사진 등)에 접근한 수 있는 권한 부여에 사용한다. 클라이언트가 처음 인증을 받게 될 때 (로그인 시) access, refresh token을 둘 다 받지만 실제로 권한을 얻기 위해 사용하는 토큰은 access token인 것이다. refresh token의 경우 access token을 재발급받을 때 사용한다. access token을 만약 다른 악의적인 유저가 얻어 내서 그것을 악의적으로 사용할 위험이 있기 때문에 access token은 비교적 짧은 유효 기간을 부여한다. 이 때문에 access token은 주기적으로 재발급해야 하는데 이때 refresh token을 사용하는 것이다. 만약 refresh token이 다른 유저에게 노출된다면 큰 문제를 일으킬 수 있다. 그래서 유저의 정보를 지키는 것이 더 중요한 웹사이트들은 refresh token을 사용하지 않는 곳도 있다.

 

  • JWT 구조

1) Header

Header는 이것이 어떤 종류의 토큰인지, 어떤 알고리즘으로 sign 할지가 적혀있다. JSON Web Token이라는 이름에 걸맞게 JSON 형태로 볼 수 있다.

{
  "alg": "HS256",
  "typ": "JWT"
}

2) Payload

Payload에는 정보가 담겨 있다. 어떤 정보에 접근 가능한지에 대한 권한을 담을 수 있고 사용자의 유저 이름 등 필요한 데이터는 이곳에 담아 Sign 시킨다. Payload에는 민감한 정보는 되도록 담지 않는 것이 좋다.

{
  "sub": "someInformation",
  "name": "seonghyeon",
  "iat": 12345
}

3) Signature

base64로 인코딩 된 첫 번째 그리고 두 번째 부분이 완성되었다면 원하는 비밀 키(암호화에 추가할 salt)를 사용하여 암호화한다. base64 인코딩을 한 값은 누구나 쉽게 디코딩할 수 있지만 서버에서 사용하고 있는 비밀키를 보유한 게 아니라면 해독해 내는데 엄청난 시간과 노력이 들어간다.

 

  • 토큰 기반 인증 절차
  1. 클라이언트가 서버에 아이디/비밀번호를 담아 로그인 요청을 보낸다.
  2. 아이디/비밀번호가 일치하는지 확인하고 클라이언트에게 보낼 Signature 된 토큰을 생성한다. (access/refresh 토큰을 모두 생성한다, 토큰에 담길 정보(payload)는 유저를 식별할 정보, 권한이 부여된 카테고리(사진, 연락처, 기타 등등)가 될 수 있다, 두 종류의 토큰이 같은 정보를 담을 필요는 없다)
  3. 토큰을 클라이언트에게 보내주면, 클라이언트는 토큰을 저장한다. (저장하는 위치는 local storage, cookie, react의 state 등 다양하다)
  4. 클라이언트가 HTTP 헤더(authorization 헤더)에 토큰을 담아 보낸다. (bearer authentication을 이용한다)
  5. 서버는 토큰을 해독하여 발급한 토큰이 맞다는 판단이 될 경우 클라이언트의 요청을 처리한 후 응답을 보내준다.

 

  • 토큰 기반 인증의 장점
Statelessness & Scalability (무상태성 & 확장성) 서버는 클라이언트에 대한 정보를 저장할 필요가 없다. (토큰 해독이 되는지만 판단한다)
클라이언트는 새로운 요청을 보낼 때마다 토큰을 헤더에 포함시키면 된다.
서버를 여러 개 가지고 있는 서비스라면 더더욱 빛을 발휘한다. (같은 토큰으로 여러 서버에서 인증 가능)
안전하다 signature을 받은 토큰을 사용하고 암호화 키를 노출할 필요가 없기 때문에 안전하다
어디서나 생성 가능하다 토큰을 확인하는 서버가 토큰을 만들어야 하는 법이 없다.
토큰 생성용 서버를 만들거나 다른 회사에서 토큰 관련 작업을 맡기는 것 등 다양한 활용이 가능하다
권한 부여에 용이하다 토큰의 payload(내용물) 안에 어떤 정보에 접근 가능한지를 정할 수 있다.

 

  • OAuth 2.0

우리가 흔히 사용하는 웹이나 흔히 찾아볼 수 있는 소셜 로그인 인증 방식은 OAuth 2 라는 기술을 바당으로 구현된다. 전통적으로 직접 작성한 서버에서 인증을 처리해 주는 것과는 달리 OAuth는 인증을 중개해 주는 메커니즘이다. 보안된 리소스에 액세스 하기 위해 클라이언트에게 권한을 제공하는 프로세스를 단순화하는 프로토콜이다. 즉, 이미 사용자 정보를 가지고 있는 웹 서비스(GitHub, google, facebook 등)에서 사용자의 인증을 대신해 주고 접근 권한에 대한 토큰을 발급한 후 이를 이용해 내 서버에서 인증이 가능해진다.

 OAuth는 인증(Authentication)을 다른 서비스에 맡길 뿐 접근 권한 관리(Authorization)와 사용자 정보가 내 서버에 저장되는 것은 변함이 없다. OAuth 2.0은 인증을 위한 표준 프로토콜의 한 종류로 보안된 리소스에 액세스 하기 위해 클라이언트에게 권한을 제공(Authorization)하는 프로세스를 단순화하는 프로토콜 중 한 방법이다.

Resource Owner 액세스 중인 리소스의 유저이다. 만약 '지성현'이 구글 계정을 이용하여 App에 로그인할 경우 이 때 Resource Owner는 '지성현'이다.
Client Resource owner를 대신하여 보호된 리소스에 액세스하는 응용프로그램이다. 클라이언트는 서버, 데스크탑, 모바일 또는 기타 장치에서 호스팅 할 수 있다.
Resource server client의 요청을 수락하고 응답할 수 있는 서버이다.
Authorization server Resource server가 액세스 토큰을 발급받는 서버이다. 클라이언트 및 리소스 소유자를 성공적으로 인증한 후 액세스 토큰을 발급하는 서버를 말한다.
Authorization grant 클라이언트가 액세스 토큰을 얻을 때 사용하는 자격 증명의 유형이다.
Authorization code access token을 발급받기 전에 필요한 code이다. client ID로 이 code를 받아온 후 client secret과 code를 이용해 Access token을 받아온다.
Access token 보호된 리소스에 액세스하는 데 사용되는 credentials이다. Authorization code와 client secret을 이용해 받아온 이 Access token으로 이제 resource server에 접근 할 수 있다.
Scope scope는 토큰의 권한을 정의한다. 주어진 액세스 토큰을 사용하여 액세스할 수 있는 리소스의 범위이다.

 

'인증,보안' 카테고리의 다른 글

HMAC  (0) 2023.03.14
[Git] 브랜치 관리와 고급 기능  (0) 2022.06.28
[인증/보안] 기초1  (0) 2022.06.23
[Linux] 권한과 환경변수  (0) 2021.12.22
Git 기초1  (0) 2021.07.06