작년에 회사에서 인턴 중간 발표때의 일이다.
당시, Spring Boot와 Thymeleaf를 활용하여 풀스택 프로젝트를 진행 중이었다. 이 과정에서 Spring Security를 활용해 로그인 기능을 개발하였는데, 중간 발표 후 팀장님께서 흥미로운 질문을 던져주셨다.
비밀번호는 어떤 암호화 방식을 사용해서 저장했나요?
당시에는 개발시간이 부족해 Spring Security의 비밀번호 인코더가 어떻게 동작하는지 깊게 알지 못하고 있었다. 그래서 간단히 해시 함수를 사용했다고만 답했었다.
만약 해싱된 비밀번호가 유출되면 어떤 위험이 있을까요?
이 질문에는 명확한 답변을 준비하지 못했다.
해시된 문자열만 유출되면 원본 문자열을 유추하기는 어렵다고 생각했으나,
실제로는 여러 위험성이 존재했다.
해시 함수란 어떤 입력값을 받아서 고정된 크기의 알파벳과 숫자로 된 문자열을 출력하는 함수를 뜻한다.
이는 단방향 함수의 특성을 가지므로, 해싱된 결과를 통해 원래의 입력값을 직접 알아내는 것은 불가능하다. 그러나 무차별 대입 공격(brute force attack)을 통해 가능성 있는 입력 문자열들을 대입해보며 해싱값을 확인함으로써 원본 문자열을 유추하는 것은 가능하다.
아래는 "당신의 비밀번호"라는 문자열에 대해 여러 해시 알고리즘을 적용한 예시다.
그러면 위의 해시 알고리즘을 사용하여 비밀번호를 암호화하면 문제가 없을까?
아니다. 길이가 짧고 보안 취약점이 드러난 알고리즘을 사용하면 문제가 될 가능성이 매우 크다.
현재 보안취약점이 발견된 MD5
와 SHA-1
은 보안목적으로 사용해선 안된다.
만약 MD5 알고리즘으로 암호화된 비밀번호를 사용하는데 해싱된 문자열이 유출된다면
몇시간안에 계정을 해킹할 수 있다. (SHA-1은 좀더 오래걸린다)
따라서 SHA-2 이상의 알고리즘을 사용하는 것이 좋다.
단순 해시 알고리즘만을 사용하는 것은 여전히 위험할 수 있다.
해커가 단순히 해싱된 비밀번호 문자열만을 탈취하는 것이 아니라 원본 비밀번호를 알면, 동일한 해시값을 가진 다른 사용자의 계정까지 접근할 수 있기 때문이다.
이런 위험을 줄이기 위해 '솔트'라는 랜덤 문자열을 원본 비밀번호에 추가하여 해싱하는 방법을 사용한다. 이렇게 하면, 같은 원본 비밀번호라도 해시값이 다르게 나오기 때문에, 해커의 공격이 어려워진다.
그러면 내가 인턴 때 사용했던 Spring Security에서는 어떻게 비밀번호를 암호화할까?
비밀번호 암호화에 PasswordEncoder
라는 인터페이스를 사용하는데
이 인터페이스의 구현체중 BCryptPasswordEncoder
라는 BCrypt알고리즘을
사용한 클래스를 많이 사용한다.
BCrypt는 다음과 같은 특징을 가지고 있어 비밀번호의 안전한 저장을 지원한다.
앞서 언급한 바와 같이, 각각의 해싱마다 솔트라는 임의의 문자열을 함께 사용한다.
BCrypt는 주어진 문자열을 여러 번 반복하여 해싱한다.
결과적으로 브루트 포스 공격에 대한 안전성이 높아진다.
비밀번호 검증 시, 사용자로부터 받은 비밀번호와 저장된 솔트를 합친 후
같은 해싱 과정을 거쳐서 저장된 해시 값과 비교한다.
일치한다면, 비밀번호가 올바른 것으로 판단한다.
암호화된 비밀번호가 노출되더라도 해커가 비밀번호를 유추하기 힘들게 하려면
Bcrypt같은 솔트처리가 된 알고리즘을 사용하는게 좋다.
개발을 할 때는 내가 사용하는 기술에 대해 충분히 이해하고 사용해야겠다는 생각이 들었다.
특히 보안쪽은 한번더 고민하는 습관을 가지자.