보통 패스워드를 DB에 저장할 때 두 가지 방법으로 저장한다.
해시 함수는 입력값을 문자와 숫자를 임의로 나열한 일정한 길이의 다이제스트(Digest)형태로 변환시켜준다. 여기서 다이제스트란, 해시함수를 통해 생성된 암호화된 메시지이다. 해시 함수는 단방향이므로 암호화는 가능하지만 복호화는 불가능하여 원본을 알 수 없기 때문에 이와 같은 이유로 패스워드를 바로 데이터베이스에 저장하지 않고 단방향 암호화된 다이제스트를 저장하는 것이 보편화되었다.
레인보우 공격(rainbow attack) - 인식 가능성
동일한 메시지가 언제나 동일한 다이제스트를 갖는다면 해커가 전처리(pre-computing) 된 다이제스트를 다량 확보한 다음 탈취한 다이제스트와 비교해 원본 메시지를 찾아내거나 동일한 효과의 메시지를 찾을 수 있다. 이와 같은 다이제스트 목록을 레인보우 테이블(rainbow table)이라 한다. 게다가 다른 사용자의 패스워드가 같으면 다이제스트도 같으므로 한꺼번에 모두 정보가 탈취될 수 있다.
무차별 대입 공격(brute force attack) - 속도
해시 함수는 원래 짧은 시간에 데이터를 검색하기 위해 설계된 것으로 해시함수의 빠른 처리 속도로 인해 해커는 매우 빠른 속도로 임의의 문자열의 다이제스트와 해킹할 대상의 다이제스트를 비교할 수 있다. (MD5를 사용한 경우 일반적인 장비를 이용하여 1초당 56억 개의 다이제스트를 대입할 수 있다)
솔팅(Salting)
패스워드에 임의의 문자열인 salt를 추가하여 다이제스트를 생성하는 것으로, 같은 패스워드라도 각기 다른 salt가 들어가 다이제스트가 다르게 생성되어 rainbow table을 무의미하게 만든다. salt는 최소 128bit 정도는 되어야 안전하다.
키 스트레칭(key stretching)
해시를 여러 번 반복하여 시간을 늘림으로써 무차별 대입 공격(brute force attack)에 대비하는 것이다. 쉽게 말하자면, 패스워드의 다이제스트를 생성하고, 생성된 다이제스트를 입력값으로 하여 또 다이제스트를 생성하고.. 이를 반복하여 다이제스트를 생성하는 식이다. 만약 원래대로라면 1초에 56억 개를 대입하여 비교할 수 있는데 키스트레칭을 적용하여 1초에 5번 정도만 비교할 수 있게 설정한다면 하나의 다이제스트를 해킹하는 데 오랜 시간이 걸릴 것이다.
MD5, SHA-1, HAS-180 : 이미 보안이 뚫린 해시 함수들
SHA-256, SHA-512 : 특정 입력값에 대해 항상 같은 해시 값을 리턴(레인보우 어택에 취약)
PBKDF2(Password-Based Key Derivation Function)
해시함수의 컨테이너인 PBKDF2는 솔트를 적용한 후 해시함수의 반복 횟수를 임의로 선택할 수 있다. PBKDF2는 아주 가볍고 구현하기 쉬우며, SHA와 같이 검증된 해시함수만을 사용한다.(Django에서 사용)
DIGEST = PBKDF2(PRF, Password, Salt, c, DLen)
# PRF: 난수(예: HMAC)
# Password: 패스워드
# Salt: 암호학 솔트
# c: 원하는 iteration 반복 수
# DLen: 원하는 다이제스트 길이
bcrypt (Key Derivation Functions)
bcrypt는 처음부터 패스워드 저장을 위해서 설계되었다. bcrypt는 보안에 집착하기로 유명한 OpenBSD에서 기본 암호 인증 메커니즘으로 사용되고 있고 미래에 PBKDF2보다 더 경쟁력이 있다고 여겨진다. 입력값을 72byte로 해야 하는 부분이 PBKDF2와 scrypt와 다르다.
scrypt (Key Derivation Functions)
scrypt는 상대적으로 최신 알고리즘이며 나머지 둘보다 더 경쟁력 있는 것으로 평가되나 아직 덜 확산되어 있다. scrypt는 다이제스트를 생성할 때 메모리 오버헤드를 갖도록 설계되어, brute force attack을 시도할 때 병렬화 처리가 매우 어렵다. 따라서 PBKDF2보다 안전하다고 평가되며 미래에 bcrypt에 비해 더 경쟁력이 있다고 여겨진다. scrypt는 보안에 아주 민감한 사용자들을 위한 백업 솔루션을 제공하는 Tarsnap에서도 사용하고 있다.
DIGEST = scrypt(Password, Salt, N, r, p, DLen)
# Password: 패스워드
# Salt: 암호학 솔트
# N: CPU 비용
# r: 메모리 비용
# p: 병렬화(parallelization)
# DLen: 원하는 다이제스트 길이
해당 내용을 정리 해놓은 곳을 찾고 있었는데 핵심만 잘 요약해주신것 같습니다. 감사합니다!