보안 강화를 위한 비밀번호 해싱

Dong·2025년 2월 6일

1차프로젝트

목록 보기
3/4

회원가입을 API를 만들때 비밀번호는 암화화 해줘야한다 권장사항이 아니라 강제 사항이다 아래는 개정된 개인정보 법이다.

왜 비밀번호는 평문으로 저장하면 안될까??

비밀번호를 평문으로 저장하면 데이터 유출 시 모든 사용자의 비밀번호가 그대로 노출되어 2차 피해가 발생할 수 있고 내부 직원이 DB를 조회해 사용자 비밀번호를 볼 수 있어 악용될 위험이 있다

그렇다면 어떤 방식으로 저장해야 탈취하더라도 개인정보를 알기가 어렵게 만들 수 있을까??

일단 암호화에 대해서 알아야하는데 암호화는 크게 양방향, 단방향 알고리즘으로 나뉜다 단반향과 양방향에 대해서 알아보자

  • 대칭키 암호화: 하나의 키로 암호화와 복호화를 모두 수행, 속도가 빠르지만 키 관리가 어려움 (예: AES, DES).
  • 비대칭키 암호화: 공개키(Public Key)로 암호화하고 개인키(Private Key)로 복호화, 보안성이 높지만 속도가 느림 (예: RSA, ECC).

대칭키 암호화는 데이터를 암호화하여 민감한 정보를 숨길 수 있지만, 키가 유출되면 평문으로 복호화할 수 있어 보안에 취약하다. 비대칭키 암호화도 양방향 암호화 방식이지만, 개인키가 유출되면 암호화된 데이터를 복호화할 수 있어 보안 문제가 발생할 수 있다.

Hash(단방향)

장점

  • 빠른 연산 속도: 해시 연산이 비교적 빠르며, 대량의 데이터 처리에 유리함.
  • 단방향성: 해시 값에서 원래 데이터를 복구할 수 없어 보안성이 높음.
  • 고정된 길이: 입력 크기와 관계없이 일정한 길이의 해시 값이 생성됨.
  • 데이터 무결성 보장: 해시 값을 비교하여 데이터 변조 여부를 쉽게 확인 가능.

단점

  • 충돌(해시 충돌) 가능성: 서로 다른 입력 값이 같은 해시 값을 가질 가능성이 있음.
  • 빠른 연산 속도가 오히려 단점이 될 수 있음: SHA-256 같은 일반적인 해시 함수는 연산이 빠르기 때문에 브루트포스 공격, 레인보우 테이블 공격에 취약할 수 있음.
  • 비밀번호 저장용으로 단순 해싱은 부적절: SHA-256 같은 해시 함수만 사용하면 해커가 쉽게 역추적 가능하므로 솔트(Salt)와 키 스트레칭이 필요함.

해시함수 종류

MD5 (Message Digest Algorithm 5)

  • 1991년에 Ronald Rivest에 의해 탄생
  • 128비트 길이의 해시 값
  • 충돌 공격에 취약하여 보안 용도로 부적절

SHA-1 (Secure Hash Algorithm 1)

  • 1993년 NIST(미국 국립표준기술연구소)에서 발표
  • 160비트 길이의 해시 값
  • 보안 취약점으로 인해 현재 사용이 권장되지 않음

SHA-2 (Secure Hash Algorithm 2)

  • 2001년 NIST에서 SHA-1의 취약점을 해결하기 위해 설계
  • SHA-224, SHA-256, SHA-512 등 여러 버전의 출력 길이 제공
  • 현재까지 강력한 보안성을 유지하며 널리 사용됨

그렇다면 어떤 해시 함수를 사용해야 할까?

비밀번호를 안전하게 보호하려면 어떤 해시 함수를 선택해야 하는지 고민해야 한다. 앞서 살펴본 것처럼, 일부 해시 알고리즘은 보안이 취약하여 현재는 사용이 권장되지 않는다.

  • MD5는 128비트 길이의 해시 값을 생성하지만, 충돌 공격에 매우 취약하여 보안 용도로 적절하지 않다.
  • SHA-1 역시 160비트 해시 값을 생성하지만, 이미 보안 취약점이 발견되어 현재는 사용이 권장되지 않는다.
  • SHA-2는 SHA-1의 취약점을 개선하여 설계된 알고리즘으로, SHA-256, SHA-512 등 다양한 출력 길이를 제공하며 현재까지 강력한 보안성을 유지하고 있다.

하지만 SHA-2조차 비밀번호 해싱용으로는 적절하지 않을 수 있다. 왜냐하면, SHA 계열 알고리즘은 설계상 빠른 연산 속도를 목표로 하기 때문이다. 해싱 속도가 빠르면 공격자 역시 더 빠르게 대량의 비밀번호를 해독할 수 있어 보안성이 떨어질 수 있다. 그렇다면 어떻게 비밀번호가 탈취될까??

레인보우 테이블

레인보우 테이블은 모든 문자열의 해시 값을 저장하는 것이 아니라, 특정 문자와 숫자의 조합에 제한을 두고 저장하는 방식이다.

또한, 레인보우 테이블의 크기를 줄이기 위해 해시 값을 다시 변환하여 저장하는 함수(Reduction Function)를 사용할 수도 있다. 이를 통해 저장 공간을 절약하면서도 해킹 공격을 효율적으로 수행할 수 있다.

공격자는 레인보우 테이블을 이용해 서버에 저장된 해시 값과 대조하여 원래 비밀번호를 추론할 수 있다.

이런 레인보우 테이블 공격을 막기위해 나온게 Salt이다

Bcrypt

위의 문제를 보안하기 위해 단방향 암호화를 진행할 때 솔팅(Salting)과 키 스트레칭(Key Stretching)을 적용 시킨다.

Salting
솔팅은 단방향 해시 함수 암호화를 진행 할 때 본래 데이터에 추가적으로 랜덤한 데이터를 더하는 방식이다. 원래 데이터에 추가 데이터가 포함 되었기 때문에 이전의 해시값과 달라진다.

Salt

레인보우 테이블 공격의 경우 특정 문자에 대한 출력해시를 입력해서 원본을 찾아내는 방식인데, Salt가 추가되면 공격자가 알게되는 원본은 '비밀번호'가 아닌 '비밀번호 + Salt'이다.

키 스트레칭(Key Stretching)
단방향 해쉬값을 계산 한 후, 그 해쉬값을 또 다시 해시하고 또 이를 반복하는 방식이다. 최근 일반적인 장비로도 1초에 50억 개 이상의 해시값을 비교할 수 있다. 하지만 키 스트레칭을 적용하면 동일 장비에서 1초에 5번 정도만 비교할 수 있다. GPU(Graphics Processing Unit)를 사용하더라도 수백에서 수천 번 정도만 비교할 수 있다.

Bcrypt 알고리즘은 비밀번호 저장을 위해 설계된 해시 함수로, 해시 값을 생성하는 데 시간이 오래 걸리는 구조를 가진다.
솔팅과 키 스트레칭을 결합한 계산 방식을 사용하며, 이러한 방식을 Adaptive Key Derivation Function이라 부른다.
이 방식은 GPU 병렬 연산을 어렵게 만들어 브루트포스 공격과 레인보우 테이블 공격을 어렵게 만든다.

구현하기

이렇게 정리한 이유는 Bcrypt를 사용하기 위해서다.
예전에는 단순히 암호화를 위해 쓰는 기술이라고만 생각했지만, 이번 프로젝트를 통해 "왜 Bcrypt를 써야 할까?" 라는 의문을 가지게 됐다.

위의 코드는 Spring Security 설정 클래스이며,
@Bean을 사용해 BcryptPasswordEncoder를 등록하여 비밀번호 암호화를 적용하고 있다
이렇게 하면 Spring 컨테이너에서 PasswordEncoder 빈을 관리하게 되어,
서비스나 리포지토리에서 비밀번호 암호화 및 검증 시 주입받아 사용할 수 있다.

String aPassword = passwordEncoder.encode(createMemberDTO.getPassword());

결과값

출처, 참고

https://deediims.tistory.com/32
https://study-easy-coding.tistory.com/153
https://blog.skby.net/%EC%95%94%ED%98%B8%ED%99%94-encryption/

profile
소통을 잘하는 백엔드 개발자가 되기 위해, 꾸준히 성장하고 기록중입니다.

0개의 댓글