Unit7. [Backend] 인증 / 보안

2023. 3. 7. 11:00코드스테이츠/코드스테이츠S3: Chapter & Unit

Cookie 란? (누가만든 쿠키이~)

서버가 웹 브라우저에 정보를 저장하고 불러올 수 있는 수단.

해당 도메인에 대해 쿠키가 존재하면,

웹 브라우저는 도메인에게 http 요청 시 쿠키를 함께 전달한다.

쿠키는 클라이언트의 로컬에 저장되는 키와 값이 들어있는 데이터 파일이다. 

(ex. 이름,값,만료 날짜/시간(쿠키 저장기간), 경로정보 등)

쿠키는 서버에서 HTTP Response Header Set-Cookie 속성을 이용하여 클라이언트에 쿠키를 제공한다.

쿠키는 삭제하지않으면 사라지지 않는다라는 특성을 가지고있다.

그렇기에 장기간 저장해야 할 옵션을 클라이언트에 저장하기에 적합하다.

그럼 쿠키는 내가 삭제하지않으면 평생남아있는가? 아니다. 

세션쿠키(Session Cookie)와 지속 쿠키(Persistent Cookie)

쿠키에는 두가지 종류로 나뉘는데, 세션쿠키(Session Cookie)지속쿠키(Persistent Cookie)가 있다.

지속쿠키는 만료기간을 설정해놓으면, 만료기간이 전까지는 프로세스가 종료되더라도 특정 만료날짜/시간까지는 지속쿠키에 저장된다. 만일 만료기간을 설정해놓지 않으면 브라우저가 열린동안만 유효한 세션쿠키로 저장되며 브라우저를 종료하면 쿠키도 삭제된다.

이렇게보면 뭐든 지속쿠키로 일단 저장해놓는게 편리하지않나? 싶었지만, 지속쿠키의 경우 파일의 형태로 저장되기 때문에 비교적 보안에 취약하다. 반대로 세션쿠키의 경우 보안상 안전한 브라우저의 메모리에 저장되기때문에 브라우저를 종료하면 휘발되긴하지만 보안면에서 유리하다. 그렇기때문에 로그인을 하게되는 포털이나 사이트의 경우 브라우저를 끄게 되면 자동으로 로그아웃이 되는거다.(로그인정보는 보안이 중요하기 때문에 지속쿠키로 관리하는건 옳지않다.)

  • 쿠키 프로세스
    1. 브라우저에서 웹페이지에 접속한다.
    2. 클라이언트가 요청한 웹페이지를 응답으로 받으면서 HTTP 헤더를 통해 해당 서버에서 제공하는 쿠키 값을 응답으로 준다. (이러면 클라이언트는 해당 쿠키를 저장한다.)
    3. 클라이언트가 웹페이지를 요청한 서버에 재 요청시 받았던 쿠키 정보도 같이 HTTP 헤더에 담아서 요청한다.
    4. 서버는 클라이언트의 요청(Request)에서 쿠키 값을 참고하여 비즈니스 로직을 수행한다. (ex 로그인 상태 유지, ...)
  • 쿠키 사용 사례
    • 30일동안 로그인 상태 유지
    • 쇼핑몰 장바구니 정보
    • 하루동안 보지않기 등
  • 쿠키의 한계
    • 클라이언트에 최대 300개 까지 쿠키를 저장할 수 있다.
    • 서버 도메인 하나당 최대 20개의 쿠키를 저장할 수 있다.
    • 하나의 쿠키 값은 최대 4KB까지 저장할 수 있다.

 쿠키 옵션 (Cookie option)

Domain

  • 서버와 요청의 도메인이 일치하는 경우 쿠키 전송

Path

  • 서버와 요청의 세부경로가 일치하는 경우 쿠키 전송

MaxAge or Expires

  • 쿠키의 유효기간 설정
  • ex. 피시방에서 로그아웃 안하고 나왔을 경우에도 유효기간이 지정되어 있기때문에 자동 로그아웃된다.

HttpOnly

  • 스크립트 쿠키 접근 가능 여부 결정
  • 쿠키는 XSS 공격에 취약한데, HttpOnly를 통해 스크립트태그로 접근하지 못하게 보안강화가능

Secure

  • HTTPS 프로토콜에서만 쿠키 전송 여부 결정

SameSite

  • CORS 요청의 경우 옵션 및 메서드에 따라 쿠키 전송 여부 결정
  • Lax : GET 메서드 요청만 쿠키 전송 가능
  • Strict : 쿠키 전송 불가
  • None : 모든 메서드 요청에 대해 쿠키 전송 가능 / None은 보안면에서 위험하기 때문에 Secure 쿠키 옵션이 필요하다.

Token이란? 

토큰을 이해하기 위해서는 해싱(Hashing)부터 알아봐야한다.

해싱이란 암호화 방식 중 하나이다. 해싱은 다른 암호화 방식과는 다르고, 암호화만 가능하고 복호화는 불가능하다.

해싱은 해쉬함수(Hash Function)를 사용하여 암호화를 진행하는데, 해시 함수는 다음과 같은 특징을 가진다.

  • 항상 같은 길이의 문자열을 리턴한다.
  • 서로 다른 문자열에 동일한 해시 함수를 사용하면 반드시 다른 결과값이 나온다.
  • 동일한 문자열에 동일한 해시 함수를 사용하면 항상 같은 결과값이 나온다.

비밀번호해시 함수(SHA1) 리턴 값

‘parksubeom’ ‘02910A5C737AACF75DE0FAB454303587E822AACF’
‘javascript’ ‘B6E13AD53D8EC41B034C49F131C64E99CF25207A’
‘helloworld’ ‘423F91670E315B277282933DAB93534EA808D378’

비밀번호를 해시함수를 통해 변환해봤는데, 같은 비밀번호라도 다른 해싱값을 리턴했다.

문자열 별로 어떠한 특정한 값이 있는 줄 알았는데 완전 임의의 값이라면 도대체 해커들은 이걸 보고 어떻게 해킹을 하는거지?!

라고 생각 할 쯔음 바로 레인보우 테이블이라는걸 알게됐다.

레인보우 테이블과 솔트(Salt)

그런데 항상 같은 결과값이 나온다는 특성을 이용해 해시 함수를 거치기 이전의 값을 알아낼 수 있도록 기록해놓은 표인 레인보우 테이블이 존재합니다. 레인보우 테이블에 기록된 값의 경우에는 유출이 되었을 때 해싱을 했더라도 해싱 이전의 값을 알아낼 수 있으므로 보안상 위협이 될 수 있습니다.

[사진] 레인보우 테이블 예시

이 때 활용할 수 있는 것이 솔트(Salt)입니다. 솔트는 소금이라는 뜻으로, 말 그대로 소금을 치듯 해싱 이전 값에 임의의 값을 더해 데이터가 유출 되더라도 해싱 이전의 값을 알아내기 더욱 어렵게 만드는 방법입니다.

비밀번호 + 솔트해시 함수(SHA1) 리턴 값

‘password’ + ‘salt’ ‘C88E9C67041A74E0357BEFDFF93F87DDE0904214’
‘Password’ + ‘salt’ ‘38A8FDE622C0CF723934BA7138A72BEACCFC69D4’
‘kimcoding’ + ‘salt’ ‘8607976121653D418DDA5F6379EB0324CA8618E6’

솔트를 사용하게 되면 해싱 값이 유출되더라도, 솔트가 함께 유출 된 것이 아니라면 암호화 이전의 값을 알아내는 것은 불가능에 가깝습니다.

 

해싱의 목적

그런데, 왜 복호화가 불가능한 암호화 방식을 사용하는 걸까요? 바로 해싱의 목적은 데이터 그 자체를 사용하는 것이 아니라, 동일한 값의 데이터를 사용하고 있는지 여부만 확인하는 것이 목적이기 때문입니다.

예시를 들어보겠습니다. 사이트 관리자는 사용자의 비밀번호를 알고있을 필요가 없습니다. 오히려 사용자들의 비밀번호를 알고 있다면, 이를 얼마든지 악용할 수 있기 때문에 심각한 문제가 생길 수도 있습니다. 그래서 보통 비밀번호를 데이터베이스에 저장할 때, 복호화가 불가능하도록 해싱하여 저장하게 됩니다. 해싱은 복호화가 불가능하므로 사이트 관리자도 정확한 비밀번호를 알 수 없게 되죠.

그럼 서버측에서 비밀번호를 모르는 상태에서 어떻게 로그인 요청을 처리할 수 있는 걸까요? 방법은 간단합니다. 해싱한 값끼리 비교해서 일치하는지 확인하는 것이죠. 꼭 정확한 값을 몰라도, 해싱한 값이 일치한다면 정확한 비밀번호를 입력했다는 뜻이 되기 때문에, 해싱 값으로만 로그인 요청을 처리하는데에도 전혀 문제가 없습니다.

이처럼 해싱은 민감한 데이터를 다루어야 하는 상황에서 데이터 유출의 위험성은 줄이면서 데이터의 유효성을 검증하기 위해서 사용되는 단방향 암호화 방식입니다.

 


Token 기반 인증이란? 

기존의 세션 기반 인증의 취약점을 극복하기 위해 나온 기술.

기존 세션기반 인증방식 Flow는 아래와 같다.

 

 

세션기반 인증 방식은 서버에 유저의 State를 저장하고 관리했는데, 하나의 서버가 아닌 다수의 서버를 이용하게된다면 서버간 세션데이터 공유가 필요하게되고, 그로 인해 서버의 부담을 줄 수 밖에 없었다.

서버의 부담을 줄일 수 있는 인증 방법은 없을까? 생각하다가 나온 방식이 바로 토큰기반 인증 방식이다.

토큰기반 인증 방식은 서버가 아닌 클라이언트에 사용자의 State를 저장하여 관리하는 방식이다.

Token이란 사용자 인증정보와 권한정보를 포함한 정보를 담은 암호화된 문자열을 뜻한다.

 

토큰기반 인증 방식의 Flow는 아래와 같다.

토큰기반 인증 방식의 장점

무상태성

  • 서버에서 유저의 상태를 관리하지 않음, 서버는 토큰의 유효성만을 검증해준다(서버의 비밀키를 이용)

확장성

  • 하나의 토큰으로 다수의 서버에 인증가능

어디서나 토큰 생성 가능

  • 토큰 생성만을 담당하는 인증용 서버를 만들 수 있음, 여러 앱을 하나의 토큰으로 인증하는 등 다양한 활용 가능

권한 부여에 용이

  • 사용자의 인증 정보뿐만 아니라 권환 정보도 담아 암호화 가능
  •  

대표적인 토큰인증 기술로는

JWT(JSON Web Token)

데이터 전송에 사용하는 토큰 기반 인증 기술로 JSON 형식으로 저장한 정보(Payload)를 암호화 하여 전송.

JWT기술로 만든 토큰은  3가지 구성요소로 나눠진다.

 

Header

  • 토큰의 종류
  • 서명에 사용된 알고리즘

Payload

  • 전달하려는 내용물
  • json객체로 유저정보,접근권한등을 담고있다.
  • base64로 인코딩하면 쉽게 디코딩 되기때문에 중요 정보는 Payload에 담지않는게 좋다

Signature

  • 토큰의 무결성을 확인 할 수 있는 부분
  •  

 


Access Token과 Refresh Token

  1. 서버는 클라이언트에서 요청이 들어오면, db와 비교하여 유효한 데이터인지 검증한다.
  2. 검증이 유효하다면 엑세스 토큰, 또는 엑세스토큰과 리프레쉬토큰을 클라이언트로 전달한다.
  3. 클라이언트는 전달받은 토큰을 담은 쿠키를 저장한다.
  4. 서버는 리다이렉트를 통해 토큰의 유효성을 검사한다.
  5. 엑세스토큰이있다면 혹시모를 토큰 위조 및 탈취의 경우를 대비해 한번 더 db와 대조한다
  6. 검증을 모두 마치면, 중요 데이터를 삭제 후 클라이언트에게 필요한데이터를 전송한다.
  7. 만약 엑세스토큰은 만료되었는데, 리프레쉬토큰은 유효하다면 검증절차 후 엑세스토큰을 재발급하여 클라이언트로 보내주고, 다시 리다이렉트를  통해 토큰의 유효성을 검사한다.
  8. 엑세스토큰과 리프레쉬토큰 모두 만료되었다면, 요청을 거부한다.
  9. 클라이언트는 서버로부터 받아온 데이터로 화면을 렌더링한다.

OAuth 란?

인증을 중개 해주는 메커니즘.

OAuth의 대표적인 예시로는 소셜로그인이 있다.

쉽고 안전하게 새로운 서비스를 이용할 수 있다.

어플리케이션 입장에서도 회원정보를 가지고 있을 필요가 없기때문에 정보유출에 대한 리스크가 줄어든다.

권한 영역을 설정할 수 있다.

 

OAuth의 주체

Resource Owner(사용자)

  • OAuth 인증을 통해 소셜 로그인을 하고싶어하는 사용자를 Resource Owner라고 부릅니다.
  • Resource는 사용자의 이름, 전화번호 등의 정보를 뜻합니다. 이러한 정보의 주인이 바로 사용자이기 때문에 Resource Owner라고 합니다.

Resource Server & Authorization Server(사용중인 서비스)

  • 사용자가 소셜 로그인을 하기 위해서 사용하는, 이미 사용중인 서비스(Naver, Kakao, Google 등)의 서버 중 사용자의 정보를 저장하고 있는 서버를 특정해서 Resource Server라고 부릅니다.
  • 이미 사용중인 서비스의 서버 중 인증을 담당하는 서버를 특정해서 Authorization Server라고 부릅니다.

Application(새로운 서비스)

  • 사용자가 소셜 로그인을 활용해 이용하고자하는 새로운 서비스는 환경에 따라서 조금씩 다르게 불립니다. 여기서는 Application이라고 부르도록 하겠습니다.
  • 경우에 따라서 Applicaiton을 Client와 Server로 세분화해서 지칭하기도 합니다.

OAuth의 인증방식

 

 

여기서 공통적으로 들어가는 "Grant Type" 이란 엑세스 토큰을 받아오는 방법이다.

각 인증방식의 Flow는 아래와 같다.

 

 

Implicit Grant Type

 

  1. 사용자가 Application에 접속합니다.
  2. Application에서 Authorization Server로 인증 요청을 보냅니다.
  3. Authorizaiton Server는 유효한 인증 요청인지 확인한 후 액세스 토큰을 발급합니다.
  4. Authorization Server에서 Application으로 액세스 토큰을 전달합니다.
  5. Application은 발급받은 액세스 토큰을 담아 Resource Server로 사용자의 정보를 요청합니다.
  6. Resource Server는 Application에게서 전달 받은 액세스 토큰이 유효한 토큰인지 확인합니다.
  7. 유효한 토큰이라면, Application이 요청한 사용자의 정보를 전달합니다.

이렇게 인증을 중개받아 새로운 서비스를 이용할 수 있게 되었습니다. 하지만 소셜 로그인에서 Implicit Grant Type은 잘 사용하지 않습니다. 기존 서비스에 로그인만 되어있다면 새로운 서비스에 바로 액세스 토큰을 내어주기 때문에 보안성이 조금 떨어지기 때문인데요. 그래서 보통은 여기에 인증 단계를 한 단계 추가한 인증 방식인 Authorization Code Grant Type을 주로 사용하게 됩니다.

Authorization Code Grant Type

  1. 사용자가 Application에 접속합니다.
  2. Application에서 Authorization Server로 인증 요청을 보냅니다.
  3. Authorizaiton Server는 유효한 인증 요청인지 확인한 후 Authorization Code를 발급합니다.
  4. Authorization Server에서 Application으로 Authorization Code를 전달합니다.
  5. Application이 Authorization Code로 발급받은 Authorization Code를 전달합니다.
  6. Authorizaiton Server는 유효한 Authorization Code인지 확인한 후 액세스 토큰을 발급합니다.
  7. Authorization Server에서 Application으로 액세스 토큰을 전달합니다.
  8. Application은 발급받은 액세스 토큰을 담아 Resource Server로 사용자의 정보를 요청합니다.
  9. Resource Server는 Application에게서 전달 받은 액세스 토큰이 유효한 토큰인지 확인합니다.
  10. 유효한 토큰이라면, Application이 요청한 사용자의 정보를 전달합니다.

Implicit Grant Type과 비교해보면, Authorization Code를 사용한 인증 단계가 추가로 있기 때문에 비교적 더 안전합니다. 또한, 원한다면 아래와 같이 토큰을 Application의 Client에 노출시키지 않고 Server에서만 관리하도록 만들 수도 있기 때문에 소셜 로그인을 구현하는 방식의 선택지가 늘어나게 됩니다.

그런데, 사용자가 새로운 서비스를 이용하다가 액세스 토큰이 만료되었을 때, 매번 이 과정을 거쳐서 액세스 토큰을 다시 발급받아야 한다면 사용자 편의성에 있어서는 좋지 않습니다. 그렇기 때문에 액세스 토큰을 발급해줄 때 리프레시 토큰을 같이 발급해주기도 합니다. 이 때, 리프레시 토큰을 사용해서 액세스 토큰을 받아오는 인증 방식을 Refresh Token Grant Type 이라고 합니다.

Refresh Token GrantType

 

Refresh Token Grant Type은 간단합니다. Authorization Server로 리프레시 토큰을 보내주면, Authorization Server는 리프레시 토큰을 검증한 다음 액세스 토큰을 다시 발급해주게 됩니다. Application은 다시 발급 받은 액세스 토큰을 사용해서 Resource Server에서 사용자의 정보를 받아오게 됩니다.