취약한 해시 알고리즘으로 비밀번호 저장

연산 비용이 부족한 비밀번호 해싱

설명

비밀번호를 평문으로 저장하면 저장소 유출 시 비밀번호가 그대로 드러납니다. MD5, SHA-1, 단순 SHA-256처럼 빠른 해시로 저장하면 공격자가 유출된 해시에 대해 많은 비밀번호 후보를 빠르게 시험할 수 있습니다.

잠재적 영향

무단 접근

비밀번호가 평문으로 저장되거나 안전하지 않은 해싱 알고리즘을 사용할 경우, 공격자는 이를 탈취하여 사용자 계정에 무단으로 접근할 수 있습니다. 이는 데이터 유출, 신원 도용, 금융 사기 등의 보안 사고로 이어질 수 있습니다.

크리덴셜 재사용(Credential Reuse)

사용자는 여러 서비스에서 동일한 비밀번호를 재사용하는 경우가 많습니다. 공격자가 탈취한 비밀번호를 이용해 다른 시스템에도 무단 접근할 가능성이 있습니다.

법적 및 규제 위반

다양한 산업 및 국가에서 사용자 데이터 보호를 위한 규제를 시행하고 있습니다. 비밀번호를 안전하게 저장하지 않을 경우 법적 처벌이나 기업의 신뢰도 하락을 초래할 수 있습니다.

해결 방법

  1. 안전한 해싱 알고리즘 사용

    • 평문 저장을 절대 금지하고, 적절한 해싱 알고리즘을 사용해야 합니다.
    • 새 시스템에서는 Argon2id 같은 비밀번호 전용 해싱 방식을 사용하세요. 기존 bcrypt나 PBKDF2도 적절한 작업량을 설정하고 서버 성능에 맞게 조정해야 합니다.
  2. 솔트(Salt) 적용

    • 비밀번호마다 새 무작위 솔트를 사용해 미리 계산한 결과를 여러 해시에 재사용하지 못하게 하세요. 솔트는 해시와 함께 보관할 수 있지만 비밀번호 추측 자체를 막지는 않습니다.
  3. FIPS-140 준수

    • FIPS-140 요구가 있다면 검증된 암호 모듈의 PBKDF2-HMAC-SHA256 구현을 사용하고 최소 600,000회를 기준으로 작업량을 조정하세요. 알고리즘 이름만으로 구현이 인증되는 것은 아닙니다.

예시

변경 전

다음은 Spring Security 5.8 방식의 JDBC 인증 설정 발췌입니다. 쿼리는 사용자명, 비밀번호 해시, 활성 여부를 요구되는 순서로 반환하는 스키마를 전제로 합니다.

java
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
  auth.jdbcAuthentication()
    .dataSource(dataSource)
    .usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
    .passwordEncoder(new StandardPasswordEncoder()); // 취약한 해싱 알고리즘 사용
}

변경 후

java
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, DataSource dataSource) throws Exception {
  auth.jdbcAuthentication()
    .dataSource(dataSource)
    .usersByUsernameQuery("SELECT * FROM users WHERE username = ?")
    .passwordEncoder(new BCryptPasswordEncoder()); // 안전한 해싱 알고리즘 사용
}

설명:

  • 변경 전: 레거시 StandardPasswordEncoder는 솔트가 있어도 SHA-256을 1,024회 반복하는 방식이라 비밀번호 추측 비용이 충분하지 않습니다.
  • 변경 후: bcrypt의 작업량을 조정해 추측 비용을 높입니다. 실제 비밀번호 길이 제한과 성능을 확인하고, 기존 해시는 로그인 시 재해싱하거나 비밀번호 재설정으로 전환하세요. 인코더만 교체해도 기존 해시가 변환되지는 않습니다.

참조