Development/Spring & Springboot

Springboot에서 BCrypt 암호화(해시화) 구현하기 feat. 시큐리티 인증 없이 BCrypt 사용하기

bbubbush 2022. 4. 23. 00:10

들어가며

스프링 시큐리티를 공부하다가 만나게 된 BCrypt 알고리즘은 어떤 특징이 있나 살펴보기 위해 포스팅을 작성했다.

 

또한 예전에 기술면접에서 BCrypt에 대해 잘못된 대답을 했던 부끄러운 기억이 생각나서 '언젠가는 잘못 대답했던 내용을 만회하기 위해 정리해서 글을 써야지' 했던 다짐을 실천하는 이유도 있다.

 

그럼 BCrypt 방식에 대해 알아보고 어떻게 코드를 작성할 수 있는지 확인해보자.

 

BCrypt 암호화란?

암호화 방식은 아래와 같이 구분할 수 있다.

 

암호화 방식의 분류

먼저 복호화가 가능한지에 따라 양방향 / 단방향으로 구분되고,

복호화할 때 사용하는 비밀키가 암호화할 때 그대로 사용되면 대칭키, 서로 다른 키를 사용하면 비대칭키가 된다.

 

BCrypt 알고리즘은 SHA 알고리즘과 마찬가지로 복호화 불가능하기 때문에 단반향 알고리즘이다. 

 

현재 사용되는 해시 알고리즘 중 가장 강력한 암호화 방식이다. 따라서 Spring security에서 채택하고 사용한다. 추가적으로 2^4~2^31 회의 반복 횟수를 설정할 수 있어서 처리되는 시간을 임의로 늘릴 수 있다. 이로 인해 무차별적인 대입을 통한 해킹 방식을 방어할 수도 있다.

 

컴퓨팅 성능이 늘어나도 반복하는 횟수를 늘림으로서 대처가 가능하므로 지속적으로 사용하기 좋은 암호화 방식이다. 

 

참고로 BCrypt 알고리즘은 SHA 알고리즘과 다르게 동일한 평문도 매번 다른 해시값으로 나타나므로 단순하게 비교하는 방식으로는 값의 동등성을 확인할 수 없다.

 

반응형

코드로 만들어보기

우선 Spring boot + Spring security가 필요하다. maven이나 gradle을 통해 준비하면 된다. 필자는 gradle을 통해 준비했다.

 

build.gradle

... 생략
dependencies {
  implementation 'org.springframework.boot:spring-boot-starter-security'
}
... 생략

 

다음은 스프링부트를 시작하는  *Application.java 파일을 찾아가 SecurityAutoConfiguration을 exclude 한다. 기본적으로 시큐리티가 적용되면 인증/인가를 사용하게 된다. 우리는 단순하게 BCrypt 암호화를 위해 적용하였으니 시큐리티의 기능은 꺼두겠다.

 

*Application.java

@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class BCryptApplication {
  public static void main(String[] args) {
    SpringApplication.run(BCryptApplication.class, args);
  }
}

 

다음은 암호화하는 서비스 메서드를 만든다. BCryptPasswordEncoder를 빈으로 등록하지 않은 이유는 반복횟수를 설정하는 방법이 생성자를 사용하는 방법뿐이기 때문이다. 그래서 아래 방법은 사용자의 요청에 따라 반복횟수를 4~12까지 설정할 수 있는 기능을 제공한다. (설정하지 않으면 기본적으로 10으로 세팅한다)

 

BCryptService.java

public String encodeBcrypt(String planeText, int strength) {
  return new BCryptPasswordEncoder(strength).encode(planeText);
}

 

 

반복횟수를 10번으로 "Hello, world"를 암호화하니 아래와 같은 값이 나왔다. 자세히 보면 몇 번 반복했는지 반복 횟수가 해시값 안에 적혀있다.

$2a$10$PDWPOmb3KGyKG1GpL78LH.0FGb3iayOEB3iqjRxuEM/qL0yzSQUdS

 

BCrypt 값 비교하기

마지막으로 BCrypt 해시값과 입력받은 평문이 서로 일치하는지 어떻게 확인하는지 살펴보겠다. 앞서 이야기한 것과 같이 BCrypt는 매번 해시값이 달라진다. 그래서 BCryptPasswordEncoder의 도움을 받아야 한다.

public boolean matchesBcrypt(String planeText, String hashValue, int strength) {
  BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(strength);
  return passwordEncoder.matches(planeText, hashValue);
}

 

matches()를 사용할 때는 첫 번째 파라미터로 평문의 텍스트, 두 번째 파라미터로 인코딩 값을 사용해야 한다.  실수하기 좋은 부분이니 기억해두면 좋다.

 

마치며

암호화 방식을 처음 접하는 개발자도 쉽게 읽을 수 있도록 개념과 코드를 간략하게 작성했다. 보통은 반복 횟수를 정해놓고 사용하기 때문에 위 코드처럼 매번 객체를 생성하지 않고 빈으로 등록하면 더 간단하게 작성할 수 있다.

 

자세한 소스코드를 참고하고 싶다면 아래 github 주소에서 볼 수 있다.

 

https://github.com/bbubbush/bb-encryption-api

 

GitHub - bbubbush/bb-encryption-api

Contribute to bbubbush/bb-encryption-api development by creating an account on GitHub.

github.com

 

읽어보면 좋은 글 : https://velog.io/@kylexid/왜-bcrypt-암호화-방식이-추천되어질까

 

왜 Password hashing시 Bcrypt 가 추천되어질까?

정확히는 bcrypt가 정답은 아닙니다. 일반적으로 공격자는 GPU를 이용하여 빠른 연산속도로 암호화 공격 시도를 합니다. 어떤 암호화 함수(sha-2, bcrypt, pbkdf2, scrypt, ..)를 쓰던 강력하지만, 충분한 시

velog.io