본문 바로가기

Spring

[Spring] BCryptPasswordEncoder

🔐 BCryptPasswordEncoder

BCryptPasswordEncoder는 Spring Security에서 가장 많이 사용되는 비밀번호 해싱 도구이다.

또한, bcrypt 알고리즘을 기반으로 비밀번호를 단방향 해싱하고, 자동으로 salt를 추가해주는 보안 알고리즘이다.

 

🤔 bcrypt 알고리즘?

bcrypt는 비밀번호를 안전하게 저장하기 위한 해싱 알고리즘이다. 비밀번호 보안을 위한 대표적인 알고리즘 중 하나이며,

Spring Security, Django, Laravel 등 거의 모든 프레임워크에서 사용되고 있다.

 

📌 bcrypt의 주요 특징

  1. 단방향 해싱: 복호화가 불가능한 해시값 생성
  2. 자동 Salt 포함: 같은 비밀번호도 다른 해시값 생성 (레인보우 테이블 공격 방지)
  3. 계산 비용 조절 가능: work factor 또는 cost factor를 통해 암호화 속도 조절
  4. 멱등성 불가: 같은 입력값도 매번 다른 결과 (보안 ↑)
  5. 보안성 높음: 현재까지 알려진 실용적인 공격 없음

🔄 해싱 구조

$2a$10$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36LFtQDQ3G4wXvQq1EFKlhW
 \_/ \__/\_______________________________________________/
  |   |                        |
알고리즘  비용(factor)         해시 값 (salt 포함)
  • $2a$: 알고리즘 버전
  • 10$: cost factor (2¹⁰ 반복 = 1024번 연산)
  • 나머지: salt + 해시된 비밀번호

🔧 Cost Factor (Work Factor)

BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12); // 2^12번 연산
  • bcrypt는 슬로우 해시 함수(slow hash function)이다.
  • cost factor는 연산 반복 횟수를 2의 제곱으로 늘려서 해킹에 걸리는 시간을 증가시킨다.
  • 일반적으로 10~12 사이 사용
  • 당연히 숫자가 높을수록 보안은 좋아지지만 처리 속도는 느려진다.

🤔 비밀번호는 왜 단방향 해싱해야 할까?

  • 비밀번호를 DB에 평문(plain text)으로 저장하면 해킹 시 모든 사용자 비번이 노출될 수 있다.
  • 단방향 해시는 입력값을 고정된 길이의 암호화된 값으로 변환하되, 역으로 복호화할 수 없기 때문에 안전하다.

✅ BCryptPasswordEncoder 사용 예제

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

public class PasswordEncoderExample {
    public static void main(String[] args) {
        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        String rawPassword = "mySecret123!";
        String encodedPassword = encoder.encode(rawPassword);

        System.out.println("Encoded Password: " + encodedPassword);

        // 비밀번호 일치 여부 확인
        boolean isMatch = encoder.matches(rawPassword, encodedPassword);
        System.out.println("Match result: " + isMatch);
    }
}
  • BCryptPasswordEncoder는 내부적으로 salt를 자동 생성해서 넣어준다.
  • → 매번 같은 비밀번호를 encode해도 다른 결과가 나옴.
  • DB에 해시된 비밀번호만 저장하고, 로그인 시 matches() 메서드로 비교한다.

⚙ 생성자 옵션

new BCryptPasswordEncoder(int strength);
  • strength: 해시 계산 비용(보안 수준) 조절
  • 기본값은 10 (높을수록 더 느림, 더 안전)
  • 일반적으로 10~12 사이면 충분

🤔 그럼 비교는 어떻게 하지?

bcrypt의 매력 포인트 중 하나가 바로 "해시 값 안에 salt랑 cost 정보까지 같이 포함되어 있다"는 것이다.

ㄴ 이 덕분에 DB에 오직 해시값 하나만 저장해도 되고, 그걸로 검증까지 다 가능진다.

 

🧪 실제 bcrypt 해시 예시

$2a$10$DOWSD9DNuwzVuvTRZjC6A.YrObI91ZgPNf4XEVXLw6vH/7PksnAfu
  • $2a$: bcrypt 버전 (버전에 따라 호환성 달라짐)
  • 10$: cost factor → 반복 횟수: 2¹⁰ = 1024번 해싱
  • DOWSD9DNuwzVuvTRZjC6A.: 22자리의 salt
  • YrObI91ZgPNf4XEVXLw6vH/7PksnAfu: 최종 해시 결과
  • Spring Security에서 제공하는 matches() 메서드는 이렇게 비교한다.

🔍 어떻게 비교하냐면

boolean isMatch = encoder.matches(rawPassword, encodedPasswordFromDB);
  1. DB에 저장된 bcrypt 해시 문자열을 matches()가 받으면
  2. 문자열에서 salt + cost 값을 자동으로 추출
  3. 로그인 시 입력된 비밀번호에 같은 salt, 같은 cost로 해싱 수행.
  4. 그 결과가 저장된 해시와 논리적으로 같은지 비교한다.

즉, encoded()는 결과가 다를 수 있지만, matches()는 정확히 일치하는지 판단할 수 있도록 설계되어 있다!

 

🔁 흐름 요약

[입력한 비밀번호] + [DB 해시에서 추출한 salt/cost 정보] → 새로 해시 → 기존 해시와 비교

 

✅ 장점

  • DB에 해시 하나만 저장하면 됨 → 별도로 salt 저장 안 해도 됨
  • 다른 유저가 같은 비밀번호를 쓰더라도 서로 다른 해시값이 됨
  • brute-force나 rainbow table 공격에 훨씬 강함

🤔 그럼 bcrypt 해시에 salt랑 cost 정보까지 다 들어있으면, 해커가 DB만 털면 비밀번호를 알 수 있는 거 아냐?

  • 정답은 ❌ "아니다. 쉽게는 못 알아낸다."
  • 왜냐하면 bcrypt는 단방향 해시이기 때문이다.

👉 해커가 알 수 있는 건:

  • bcrypt 해시 ($2a$10$...)
  • salt (해시 안에 있음)
  • 해싱 알고리즘과 cost 값

❌ 하지만 알 수 없는 건:

  • 해시를 거꾸로 되돌려서 원래 비밀번호를 알아내는 방법은 없음(단방향 해시)
  • bcrypt는 설계 자체가 복호화가 불가능한 구조이다.
  • 그리고 bcrypt는 단순 SHA처럼 빠른 게 아니라, ⏱ intentionally 느리게 계산된다. (보통 2^10 ~ 2^12번 반복).
  • → 해커가 비밀번호 하나 추측해볼 때마다 수 ms ~ 수백 ms 걸린다.
  • → 💸 수백만 개 대입하려면 시간/비용 엄청나다. (Brute-force 어려움)

😈 해커가 할 수 있는 건?

for (추측한 비밀번호 후보들):
    bcrypt.hash(후보, 해시에서 추출한 salt)
    해시값이 DB에 저장된 거랑 같으면 성공
  • 이론적으로는 brute-force 시도
  • 그런데 비밀번호가 복잡하면 성공률 급하락
  • bcrypt는 느림 → 대량 시도는 시간/비용 부담

💥 시나리오:

“해커가 로그인하는 순간 사용자가 입력한 비밀번호 원문(password) 를 탈취했다!
그리고 이미 DB도 털려서 bcrypt 해시값 ($2a$10$...) 도 가지고 있다.”

그런데 중요한 건 이것이 아니다.

 

🔑 1. 그 비밀번호를 원래부터 알고 있었어야 한다는 점

  • 해커가 "확인"할 수 있다는 건, 이미 비밀번호를 어딘가에서 가로챘다는 뜻이다.
  • 그럼 bcrypt는 그 자체로 더 이상 방어선이 아니게 되는 상황이에요.
  • → 이건 암호 알고리즘의 취약점이 아니라,로그인 정보를 가로챈 보안 사고인 거예요.

예: 키로깅, 네트워크 스니핑, 브라우저 취약점 등

 

 

🔐 2. bcrypt는 비밀번호를 유출되지 않도록 저장하는 거지 유출 이후를 막는 건 아니다.

 

  • 비밀번호가 탈취된 순간부터는 bcrypt고 뭐고 의미가 없음
  • bcrypt는 "DB가 털려도 안전하게 저장하는 용도"
  • 네트워크 상의 보안은 TLS(HTTPS), WebAuthn, MFA 같은 걸로 따로 보강해야 한다.

 


 

 

'Spring' 카테고리의 다른 글

[Spring] 스프링 빈과 스프링 컨테이너  (0) 2025.01.12
[Spring] 예외 처리 방식  (0) 2024.12.11
[Spring] Security JWT  (0) 2024.11.10
[Spring] 세션, 토큰, 쿠키  (0) 2024.11.08
[Spring] @Profile  (0) 2024.11.04