오늘도 개발

인증(Authentication) - Bcrypt 본문

웹 프로그래밍/웹의 이해

인증(Authentication) - Bcrypt

Sueeeeeee 2022. 7. 11. 11:24

1. 개요

인증이란?

유저의 아이디와 비밀번호를 확인하는 절차.

우리 서비스 유저가 맞는지 확인하는 절차.

 

인증에 필요한 것

아이디, 비밀번호 등. 비밀번호가 제일 중요

 

비밀번호 관리 방법

원본으로 DB에 저장하면 안되고 꼭 암호화해서 저장해야 함.

그냥 저장하면

- 개인정보보호법 위반

- DB가 해킹당하면 유저 비밀번호도 노출됨

- 내부 인력에게도 유저 비밀번호가 노출됨

 

2. 단방향 해쉬 함수

단방향 해쉬 함수(one-way hash function)란?

비밀번호를 암호화 하는 방법.

원본 메시지를 암호화할 수는 있지만 복호화할 수는 없기 때문에 단방향이라고 함.

- 방법 : 원본 메시지 -> 해쉬 함수 적용 -> 다이제스트(=digest, 암호화된 결과물) 생성

- 예 : MD5, SHA-1(둘은 보안취약), SHA-256 등

 

취약점

1) 동일한 비밀번호는 동일한 다이제스트를 가짐

=> 해커가 레인보우 테이블 사용 시 취약

 

MD5로 1234를 해싱하면 무조건 결과는 03ac543543590가 나온다고 치자.

해커는 03ac543543590라는 해시값을 보면,

다이제스트 리스트(레인보우 테이블)에서

무엇을 해시하면 03ac543543590이 나오는지 찾기만 하면 된다.

 

2) 빠르게 다이제스트를 얻을 수 있음

=> 브루트 포스(무차별 대입 공격)에 취약

 

해쉬 함수는 원래 빠르게 데이터를 검색하기 위해 만든 함수이다.

암호화 용도로 비밀번호를 넣어도 해쉬 함수는 빠르게 다이제스트를 내놓는다.

우리도 다이제스트를 빠르게 얻을 수 있지만, 해커 또한 빠르게 다이제스트를 얻을 수 있다.

해커는 빠르게 무작위 데이터를 생성하고 대입하며 우리의 비밀번호 해쉬와 대입해볼 수 있다. 

 

1을 보완하기 위해선 salting,

2를 보완하기 위해서 keystretching과 같은 방법을 같이 사용해야 한다.

3. Salting, Key Stretching

Salting 

유저가 입력한 비밀번호에 랜덤 문자열을 더하는 것.

 

비밀번호 설정 시

1) 유저가 비밀번호 입력 

2) 비밀번호에 랜덤 문자열을 더함(Salting)

3) 단방향 해쉬 함수 사용

4) 결과를 DB에 저장

 

로그인 시

1) 유저가 비밀번호 입력

2) 해당 유저에게 적용된 랜덤 문자열 더함

3) 단방향 해쉬 함수 사용

4) DB에 저장된 값과 일치하는지 확인

Key Stretching 

단방향 해쉬 함수로 얻은 값을 다시 단방향 해쉬 함수에 넣는 일을 반복하는 것.

브루트 포스(무작위로 데이터를 넣는 것)로 비밀번호 값을 찾아내는 시간을 더 오래 걸리게 할 수 있다. 

4. Bcrypt

Salting, Key Stretching, 단방향 해쉬 함수 사용을 한 번에 사용할 수 있게 해주는 라이브러리.

회원가입 시 : bcrypt.hashpw(인코딩한 비밀번호, 솔트 값)

bcrypt 메서드는 인코딩된 문자열만 인수로 받는다.

(인코딩은 Unicode로 저장된 문자열을 bytes로 바꾸는 것을 뜻한다.)

bcrypt 메서드에 넣을 때는 인코딩해서, 

메서드로 얻은 결과는 디코딩해서 사용해야 한다.

import bcrypt

password = '1234'
# 1) 인코딩
encoded_password = password.encode('utf-8')

# 2) 솔팅
salt = bcrypt.gensalt()
# b'$2b$12$92rFFIGcLEK/'

# 3) 해싱
hashed_password = bcrypt.hashpw(encoded_password, salt)
# b'$2b$12$92rFFIGcLEK/slvz3bTx/uv6kII9lkIhWOpQmA2nfqvE/EjCkcfGO'
# b는 bytes를 나타내는 뜻으로 비밀번호 일부가 아님. 디코딩하면 사라짐.
# 솔트값 + 해시값을 한 문자열로 저장함 

# 4) 디코딩
decoded_pw = hashed_password.decode()

# 5) DB에 저장
User.objects.create(name='Snoopy', password=decoded_pw)

로그인 시 : bcrypt.checkpw(인코딩한 db 비밀번호, 인코딩한 비밀번호)

import bcrypt

# 1234가 id 1번 유저의 비밀번호인지 확인

# 1) 인풋 비밀번호 인코딩
input_password = '1234'.encode('utf-8')

# 2) DB에 있는 비밀번호 인코딩
db_password = User.objects.get(id=1).password.encode('utf-8')

# 3) bcrypt로 확인
bcrypt.checkpw(db_password, input_password) 
# True나 False 반환
# db_password의 문자열은 '솔트값' + '솔트값에 비밀번호를 더해서 해시한 값'으로 이루어짐
# 'db_password의 솔트값 + input_password'를 해시한 값으로 일치 여부 확인

 

 

참고

패스워드의 암호화와 저장

'웹 프로그래밍 > 웹의 이해' 카테고리의 다른 글

RESTful API  (0) 2022.07.20
인가(Authorization) - SSS, JWT  (0) 2022.07.12
인터넷과 웹의 역사  (0) 2022.06.21
URI와 URL의 차이  (0) 2022.06.04
OSI 참조 모델과 TCP/IP  (0) 2022.05.26