웹 개발자라면 필수로 이해하고 있어야 하는 지식..! 각 절차와 암호화, JWT등에 대해서 배워보았던 세션 👩💻
학습목표
인증
이란 무엇인가?비밀번호 암호화
는 어떻게 이루어지는가?단방향 해쉬함수
&레인보우 테이블 어택
설팅
과키스트레칭
, 그리고bcrypt
인가
란 무엇인가?JWT
란?
- User의 identification을 확인하는 절차
: 회원가입, 로그인 시 아이디, 이메일주소, 비밀번호, etc 확인하는 절차- why?
: 우리 서비스를 누가 쓰는지
: 어떻게 사용하는지
추적 가능하도록 인증이 필요!
인증
을 하기 위해 나는 지금까지 회원가입 기능 구현
을 해왔던 것...!
항상 암호화 복호화 이런게 궁금하긴 했는데, 직접 구현해본 적은 한번도 없었다🥺🥺
이번 기회에 드디어..! 해보는구나...!
우선 로그인 절차는 어떻게 되는지 생각해보자.
지금까지 로그인은 그저 아이디&비밀번호를 입력
받고, DB에서 일치
하는 것이 있는지 확인한 후, 로그인 성공처리
를 하는 것일 뿐이었다.
과연 인증 절차가 포함된 로그인 절차는 어떻게 될까?
- 유저 아이디와 비번 생성
- 유저 비번 암호화 해서 DB에 저장.
- 유저 로그인 -> 아이디와 비밀번호 입력
- 유저가 입력한 비밀번호 암호화 한후 암호화되서 DB에 저정된 유저 비밀번호와 비교.
- 일치하면 로그인 성공
- 로그인 성공하면
access token
을 클라이언트에게 전송.- 유저는 로그인 성공후 다음부터는
access token
을 첨부해서 request를 서버에 전송함으로서 매번 로그인 해도 되지 않도록 한다.
- 본래
hash 함수
는 자료구조에서 빠른 자료의 검색, 데이터의 위변조 체크를 위해 쓰이지만
복원이 불가능한 단방향 해쉬함수는 암호학적 용도로 사용- (MD5, SHA-1 - 보안 취약), SHA-256 등
- 단방향 해시 함수는 원본 메세지를 변환하여 암호화된 메세지인
digest
를 생성한다.
: 원본을 알면 암호화된 메세지를 구하기 쉽지만, 암호화된 메세지만으로는 원본 메세지를 구할 수 없어서one-way
In [21]: import hashlib - In [22]: m = hashlib.sha256() - In [23]: m.update(b"test password") - In [24]: m.hexdigest() Out[24]: '0b47c69b1033498d5f33f5f7d97bb6a3126134751629f4d0185c115db44c094e' - In [25]: m = hashlib.sha256() - In [26]: m.update(b"test password2") - In [27]: m.hexdigest() Out[27]: 'd34b32af5c7bc7f54153e2fdddf251550e7011e846b465e64207e8ccda4c1aeb'
실제 비밀번호는 비슷하지만, 해시함수 값은 완전히 틀리다
:avalance
- 비밀번호 해쉬값 해킹을 어렵게 만듦
- '24'를 SHA-256으로 해싱하면 👉🏻 겁나 길다
- 결과는 완벽해보인다(당장 식별이 불가능하므로)
하지만 같은 알고리즘으로 다시 '24' 라고 해싱하면 항상 같은 결과가 도출된다.
Rainbow Table attack
👉🏻 가능한 모든 경우의 해시값을 미리 유추해둔 테이블
SALTING & KeyStretching
단방향 해쉬 함수의 취약점을 보완하기 위해 사용되는 2가지!salting
- 실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 해시값 계산하는 방법
key stretching
- 단방향 해쉬값을 계산한 후 그 해쉬값을 또 해쉬하고.. 계속 반복하는 것!
이러한 salting, key stretching을 구현한 해쉬 함수 중 가장 널리 사용되는 것이 바로 bcrypt
!
bcrypt
Salting & Key Stretching 대표적 라이브러리
- bcrypt는 앞서 말한 개념들을 실제로 적용하기 편하게 해주는 대표적인 라이브러리
- 다양한 언어 지원, 쉽게 적용 가능..!
- bcrypt는 hash 결과값에 소금값, 해시값 및 반복횟수를 같이 보관하기 때문에
비밀번호 해싱 적용에 있어 DB 설계를 복잡하게 할 필요가 없다- bcrypt를 통해 해싱된 결과값(Digest)의 구조는 이런 식..! 👇🏻👇🏻
- 유저가 로그인에 성공한 후 백엔드 서버가
access token
라는 암호화된 유저 정보를 첨부해서 클라이언트에게request
를 보낸다.## 유저 로그인 POST /auth HTTP/1.1 Host: localhost:5000 Content-Type: application/json { "username": "joe", "password": "pass" } ## access token HTTP/1.1 200 OK Content-Type: application/json { "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZGVudGl0eSI6MSwiaWF0IjoxNDQ0OTE3NjQwLCJuYmYiOjE0NDQ5MTc2NDAsImV4cCI6MTQ0NDkxNzk0MH0.KPmI6WSjRjlpzecPvs3q_T3cJQvAgJvaQAPtk1abC_E" }
프론트 서버에서
access token
을 통해복호화
후 사용자 및 정보 확인 후{ user_id = 1 }
브라우저의 storage에 저장 후 다음 요청마다 비교!
이렇게 access token
생성 방법은 여러가지가 있는데, 가장 널리 사용되는 기술중 하나가 바로 JWT (JSON Web Tokens)
👉🏻 유저 정보를 담은 JSON 데이터를 암호화해서 client, server 간 주고받는 것!
- header
: 토큰의 타입, 해시알고리즘 정보 포함(복호화를 위해)- payload
: exp 등 만료시간을 나타내는 공개 클레임
: 클라이언트, 서버간 협의하에 사용하는 비공개 클레임 (비공개 유저정보)- 서명
: JWT가 원본 그대로라는 것을 확인할 때 사용
: BASE64URL로 인코딩 된 header, payload, JWT secret을 헤더에 지정된 암호 알고리즘으로 암호하하여 전송
프론트엔드가 JWT를 백엔드 API 서버로 전송하면 서버에서는 전송받은 JWT의 서명부분을 복호화하여
서버에서 생성한 JWT가 맞는지 확인
- 사용자가 서버에 로그인하면 해당 사용자가 맞는지 확인하는 과정!
: user가 요청하는request
를 실행할 수 있는 권한이 있는 유저인가?
👉🏻 해당 유저는 고객 정보를 볼 수 는 있지만 수정할 수는 없다.. etc- 인가 또한 JWT를 통해 구현될 수 있다.
:access token
을 통해 해당 유저 정보를 얻을 수 있으므로
해당 유저가 가지고 있는 권한(permission)도 확인할 수 있다.
- Authentication 절차를 통해
access token
을 생성한다.access token
에는 유저 정보를 확인할 수 있는 정보가 들어가 있어야 한다 (예를 들어 user id).- 유저가 request를 보낼때
access token
을 첨부해서 보낸다.- 서버에서는 유저가 보낸
access token
을 복호화 한다.- 복호화된 데이터를 통해 user id를 얻는다.
- user id를 사용해서 database에서 해당 유저의 권한(permission)을 확인하다.
- 유저가 충분한 권한을 가지고 있으면 해당 요청을 처리한다.
- 유저가 권한을 가지고 있지 않으면 Unauthorized Response(401) 혹은 다른 에러 코드를 보낸다.
- HTTP의 특징은?
: request / response 요청 & 응답
: stateless한 성질 (저장하지 않는 성질)
👉🏻 각각이 독립적 / 이전의 요청을 기억하지 못함- 서버는 사용자가 로그인 했을경우, 'headers'에 메타데이터를 보내서 확인