[Docs] 비밀번호 저장 방식의 역사
Spring Security - Password Storage History
처음에는 비밀번호를 평문으로 저장했다. 저장된 비밀번호에 접근하려면 열쇠가 있어야 하므로 안전하다고 생각했다. 하지만 악의적인 사용자들이 데이터베이스를 통째로 훔쳐가기 시작했고, 평문으로 된 정보는 공공재가 되어버렸다. 보안 전문가들은 비밀번호를 보호하기 위해 무언가를 더 해야 할 필요성을 깨달았다.
개발자들은 해시를 사용하기 시작했다. 비밀번호의 해시값만 저장해두고, 사용자가 비밀번호를 입력하면 해시값을 구해 저장된 해시값과 비교한다. 그래서 단방향 해시만 필요한 것이다. 비밀번호가 유출되어도 해시값만 노출된다. 해시값만 가지고 원래의 비밀번호를 계산하기는 거의 불가능하기 때문에 원래의 비밀번호가 무엇인지는 그다지 중요하지 않다. 악의적인 사용자들은 이 시스템을 깨기 위해 룩업 테이블을 만들었다.
룩업 테이블의 효과를 줄이기 위해 개발자들은 솔트를 사용하기 시작했다. 해시의 입력으로 비밀번호만 넣는 것이 아니라 모든 사용자에게 랜덤으로 생성되는 솔트값을 함께 넣은 것이다. 솔트는 데이터베이스에 저장되고, 사용자가 비밀번호를 입력하면 솔트와 함께 해시값을 구한다. 솔트가 유니크하다는 것은 룩업 테이블이 더 이상 쓸모없다는 뜻이다.
하드웨어 성능이 매우 발달한 지금, 단순한 암호화 해시는 더 이상 안전하지 않다. 1초만에 수십억개의 비밀번호 해시를 계산해 낼 수 있기 때문이다.
이제 개발자들은 비밀번호 저장에 적응형 단방향 함수를 사용한다. 이 함수는 비밀번호 검증에 의도적으로 많은 리소스(CPU, 메모리 등)를 사용한다. 이 함수는 “work factor”를 설정하여 하드웨어가 발전함에 따라 더 많은 리소스를 사용하도록 할 수 있다. “work factor”는 비밀번호 검증에 1초가 소비되도록 설정하는 것이 권장된다. 이것은 시스템 리소스를 적당히 사용하면서도 공격을 어렵게 만든다. 적응형 단방향 함수의 예로는 bcrypt, PBKDF2, scrypt, argon2 등이 있다.
적응형 단방향 함수가 의도적으로 리소스를 많이 사용하기 때문에 로그인 요청마다 비밀번호를 검증하는 것은 앱 성능을 크게 저하시킬 수 있다. 리소스를 많이 사용해야 안전하기 때문에 마음대로 리소스 사용을 줄일 수는 없다. 그래서 장기 자격 증명을 단기 자격 증명으로 교환하는 것이 권장된다. 단기 자격 증명은 보안성 손실 없이 신속하게 검증할 수 있다.