[TIL] 2020.09.09 인증, bcrypt

dev.soo·2020년 9월 9일
0

TIL

목록 보기
5/10
post-thumbnail

Authentification : 인증. User 의 identification 을 확인하는 절차
Authorization : 인가. User 가 request 를 실행 할 권한이 있는지 확인하는 절차

인증과 인가는 API 에서 가장 자주 구현되는 기능 중 하나이다. 아직 장고를 시작한지 얼마 되지 않아 나의 user.views 는 매우 지저분하지만 이번에 배운 인증을 적용시키는 공부를 하고 있다.

그 전에 용어들을 살펴보자.

암호학적 정의
암호학을 이용하여 보호해야 할 메시지를 평문(平文, plaintext)이라고 하며, 평문을 암호학적 방법으로 변환한 것을 암호문(暗號文, ciphertext)이라고 한다. 이때 평문을 암호문으로 변환하는 과정을 암호화(暗號化, encryption)라고 하며, 암호문을 다시 평문으로 변환하는 과정을 복호화(復號化, decryption) 라고 한다.

Encryption vs Hash
암호화(Encryption)와 해쉬(Hash)
암호화는 암호화 알고리즘을 이용하고, 인증은 해쉬함수를 이용한다. 암호화 알고리즘과 해쉬 함수의 알고리즘은 동작 방식이 다르다.
암호화 알고리즘 : 양방향 통신을 전제로, 복호화를 위해 암호화 하는 것
해쉬 함수 알고리즘 : 메세지를 고정된 길이의 문자열로 변환, 복호화 필요 X

Authentification 인증

유저의 아이디와 비밀번호를 생성하는 기능 구현 후, 유저의 아이디와 비밀번호를 확인하는 절차 즉 로그인 절차라고 생각하면 된다.

회원가입, 로그인, 로그인 후 활동이 어떤 절차로 이루어 지는지 생각해보면

  1. 회원가입

    1) 유저 아이디와 비밀번호 입력
    2) 비밀번호를 암호화하여 아이디와 함께 DB에 저장하여 유저 정보 생성

  2. 로그인
    1) 유저가 생성했던 아이디와 비밀번호를 입력
    2) 유저가 입력한 아이디와 비밀번호를 DB에 저장된 아이디와 암호화되어 저장된 비밀번호와 비교하여 일치하는지 확인
    3) 일치하는 경우 server 에서 client로 access_token 을 전송 - 인가 시작

Authorization 인가

  1. 로그인 한 유저의 활동
    1) 로그인 이후 모든 request 전송시 access_token 을 첨부해서 서버로 전달하여 매번 로그인하지 않음
    2) 매 request 가 전송될 때 access_token 이 첨부되었는지 확인해서 유저의 권한만큼 response 를 알맞게 보내줌 (인터넷 주소창 왼쪽에 자물쇠 모양이 있으면 인증서 적용 중!)

웹은 위의 과정으로 유저의 정보와 활동을 저장하고 제어한다. 이 중요한 활동에서 핵심이 되는 건 암호화와 토큰 발행이다.

유저의 비밀번호는 처음 가입할 때 암호화되어 DB에 저장된다. 해커나 개발자들에게 비밀번호가 그대로 노출되지 않도록 하기 위함이다. 이 암호화의 방향은 단방향, 양방향 두 가지가 있다.

1. One-way hash function 단방향 해쉬 함수

들어올땐 맘대로지만 나갈 땐 아닌 것처럼, hasing은 쉽지만 hasing된 메세지를 원본 메세지로는 구할 수 없는 것, 암호화하는 방향만 가능한 한 성질을 단방향성이라고 한다. digest/hash 두 단어를 섞어서 말하는 것 같다.

A message digest (or hash) function is a cryptographic primitive used for digital signatures and password protection.

취약성과 보완 방법

단방향 해쉬 함수는 어떤 메세지를 입력했을 때, hasing 이후엔 언제나 같은 digest message 가 되기 때문에, 전 세계에서 제일 많이 사용한다는 비밀번호인 "password"같은 비밀번호들의 digest message를 미리 알고 있다면 비밀번호를 쉽게 알아낼 수 있다. 이렇게 미리 작성한 테이블을 Rainbow table 이라고 하는데, for 문으로 돌리면 언젠가 어떤 비밀번호는 무조건 알 수 있게 되어버린다.

이를 보완하기 위해서는 일반적으로 두 가지 방법을 적용한다.

1. Salting

  • 실제 비밀번호 이외에 추가적으로 랜덤 데이터를 더해서 hasing 하는 방법.



2. Key Stretching

  • 한 비밀번호에 단방향 해쉬 함수를 여러번 적용해서 hasing 하는 방법




    장고에 코드 구현하기

    노가다로 구현한 나의 코드에 Salting 을 사용하여 비밀번호를 암호화 해서 데이터에 저장하고, 로그인 할 때도 입력한 비밀번호와 암호화된 비밀번호를 비교하도록 하겠다.

  1. 라이브러리 : 설치하고 view 파일에 import 하기
  2. 설치된 라이브러리 사용해서 입력된 비밀번호 암호화해서 저장하기
  3. 로그인시 입력되는 비밀번호와 DB에 저장된 암호화된 비밀번호 비교하기

1. 라이브러리 import

1) 암호화를 위한 라이브러리 Bcrypt 를 설치한다.
pip install bcrypt

2) view.py 에 라이브러리를 import해준다.
import bcrypt

2. 비밀번호 암호화 해서 저장하기

코드를 바로 작성하기 전에, 함호화 과정에 따른 데이터타입을 우선 살펴보자.
password = 1234

1) DB 에 저장하고 싶은 비밀번호 입력

password = '1234'

type(password) -> str

2) 암호화를 위해(bcrypt의 인자는 bytes type 이어야 함) 비밀번호를 utf-8로 인코딩함

password_encoded = password.encode('utf-8')

type(password_encoded) -> Bytes


3) 인코딩한 비밀번호를 암호화함

password_encoded_hashed =
bcrypt.hashpw( password_encoded, bcrypt.gensalt() )

type(password_encoded_hashed) -> Bytes

4) 암호화된 비밀번호를 DB 에 저장하기 위해 str 으로 디코딩함

password_encoded_hashed_decoded =
password_encoded_hashed.decode('utf-8')

type(password_encoded_hashed_decoded) -> str

3. 로그인시 입력되는 비밀번호와 DB에 저장된 암호화된 비밀번호 비교하기

위에 DB 에 저장된 string type 의 비밀번호와 비교하기 위해 bycrpt 라이브러리의 checkpw() 메쏘드를 사용할거다. 역시 데이터 타입도 함께 작성한다.


1. 비밀번호 입력

input_password = '1234'

type(input_password) -> str




2. 입력된 비밀번호를 bycrpt.check() 사용하기 위해 인코딩

input_password_encoded = input_password.encode('utf-8')

type(input_password_encoded) -> Bytes




3. 저장되었던 비밀번호를 bycrpt.check() 사용하기 위해 인코딩

db_password = password_encoded_hashed_decoded

db_password_encoded = db_password.encode('utf-8')

type(db_password_encoded) -> Bytes


4. 각각 인코딩된 두 비밀번호를 비교

bcrypt.checkpw(input_password_encoded,db_password.encoded)

끝!!!! 3단계를 적용한 내 코드는 다음과 같다.

그리고 200OK 를 기다리다가 청천벽력같은 메세지를 만났다.

1406, "Data too long for column 'password' at row 1"

씌익씌익..
분명 암호화 전에 mysql 연결 없이 python shell 에서 회원가입 할 때는 잘 됬는데, 같은 비밀번호를 넣어도, '123'이라고 쓴 비밀번호를 넣어도 데이터가 db 에 저장되지 않았다.
알고보니, models 에서 max_length = 50 으로 설정해뒀는데 암호화가 되면서 엄청나게 길어진 데이터를 저장하지 못해서 발생하는 에러였다. 그래서 이제 암호화를 할 줄 알게 되었으니 비밀번호의 max_length 는 충분히 길게 해주어야 한다는 걸 배웠다.

고수들이 올려둔 예시의 비밀번호 max_length 가 보통 300 이었는데, 나는 최대 8자리만 적게 할건데 굳이.. 라는생각에 50으로 줄였던게 화근이었다.
아직 추후에 뭘 하게될지 몰라 큰그림을 못보고 짜다보니 이런 일이 생겼다. 오늘의 교훈은 그래서, "코딩은 나비효과다" 너로 정했다!!

--- [2편. 인가]로 이어짐

참고한 사이트
https://brownbears.tistory.com/73

0개의 댓글