비밀번호 찾기 기능은 필요할까?
2025-11-29
@khkim6040
2025-11-29
@khkim6040
POPO 서비스에 비밀번호 찾기 기능 요청이 들어왔다. 현재는 비밀번호를 잊어버렸을 때 랜덤한 비밀번호로 초기화해 주고 이를 메일로 알려주는 기능을 제공하고 있다. 하지만, 이 방법은 초기화된 비밀번호를 입력해서 로그인한 후 비밀번호를 변경해야 하는 번거로움이 있다. 번거롭게 하지 않고 비밀번호를 바로 알려준다면 편리할 것 같다.
하지만, 비밀번호를 알려주는 기능은 보안에 악영향을 미친다고 생각한다. 왜 이 단순한 기능이 비밀번호 초기화 기능에 비해 보안에 안 좋은지 알아보겠다.
먼저, 비밀번호가 어떻게 데이터베이스에 저장되는지 알아봐야 한다. 함께 비밀번호 저장 전략을 세워보자. 전략을 세우기에 앞서, 보안은 아무리 두텁게 해도 모자란다는 것을 명심하면서 함께 생각해 보자.
먼저, 단순하게 비밀번호 자체를 변경 없이 평문으로 DB에 저장하는 경우를 생각해 보자. 어차피 DB는 소수의 관리자만 접근할 수 있으니 DB가 털리지 않는다면 위험이 없다.
만약 털린다면? 조지는 것이다. 보안의 깊이가 너무 얕다.
그렇다면, DB가 털려도 사용자 비밀번호를 알 수 없게 해야 한다. 여기서 암호화가 사용된다. 관리자 소수만 아는 비밀 키를 이용해서 모든 비밀번호를 암호화하고 DB에 저장할 수 있겠다. 비밀키로 평문 비밀번호를 암호화해 DB에 저장하고, 로그인할 때는 DB에 저장된 암호화된 비밀번호를 복호화해서 사용자가 입력한 평문 비밀번호와 비교함으로써 인증을 수행할 수 있다. DB가 털려도 비밀 키를 모른다면 사용자의 비밀번호를 알 수 없게 된다. 좋다.
하지만, 여기도 한계는 있다. DB가 털리고 해커가 암호화된 비밀번호를 갖고 있을 때, 가능한 모든 문자열을 가능한 모든 비밀 키를 이용해서 암호화한 뒤, 암호화된 비밀번호 중 하나와 일치하는지 확인해 보는 방식으로 비밀 키를 알아맞힐 수 있다. 가능한 쌍이 무한대로 될 것이니 상관없다고 생각할 수도 있지만, 시간이 주어진다면 컴퓨터를 돌려서 알아내는 것도 큰 일은 아니다. 비밀키를 그렇게 얻었다면 다른 암호화된 비밀번호는 비밀 키를 적용해 손쉽게 복호화할 수 있다. 개인정보 다 털렸다.
위 문제의 핵심은 비밀 키만 알면 모든 비밀번호를 복호화할 수 있다는 것이다. 그렇다면 아예 복호화가 불가능하게 만들면 어떨까?
여기서 단방향 변환 방식이 사용된다. “password123”이라는 비밀번호를 “x9$mK2@pLq4”처럼 완전히 다른 문자열로 바꿔서 저장하되, 이 과정을 되돌릴 수 없게 만드는 것이다. 로그인할 때는 사용자가 입력한 비밀번호를 같은 방식으로 변환해서 DB에 저장된 값과 비교한다. 복호화가 불가능하므로 비밀 키를 알아내더라도 다른 비밀번호를 곧바로 복호화할 수 없게 된다.
하지만 이것만으로는 부족하다. 해커가 흔한 비밀번호들을 하나씩 변환해 보면서 DB의 값과 맞는지 확인할 수 있기 때문이다. 이를 막기 위한 추가 방법이 필요하다.
보완 방법 1: 느린 변환 방식을 사용한다.
해커가 가능한 비밀번호 문자열들을 변환해서 DB에 저장된 값 중 하나와 매칭되는지 확인할 것인데, 변환 과정 자체를 의도적으로 느리게 만들면 모든 가능한 조합을 시도하는 데 몇백 년씩 걸리게 만들 수 있다.
보완 방법 2: 각 비밀번호마다 고유한 랜덤 값을 추가해 변환한다.
같은 “password123”이라는 비밀번호라도 사용자마다 다른 랜덤 값이 붙어서 변환되므로, 최종적으로 DB에 저장되는 값이 모두 다르게 된다. 이렇게 하면 한 비밀번호를 알아냈다 하더라도, 다른 사용자의 비밀번호를 알아내려면 처음부터 다시 느린 변환 과정을 반복해야 한다. 또한 미리 계산해둔 변환 값 목록도 무용지물이 된다.
정리하자면, DB가 털리더라도 단방향 변환 덕분에 복호화가 불가능하다. 느린 변환 방식 덕분에 비밀번호를 맞추는 데도 엄청난 시간이 걸린다. 정말 운 좋게도 한 비밀번호를 알아냈다 하더라도, 각 비밀번호마다 고유한 랜덤 값이 있어서 다른 비밀번호를 알아내려면 처음부터 다시 느린 과정을 반복해야 한다.
이제 비밀번호 찾기 이야기를 해보자. ‘비밀번호를 찾는다’라는 말은 평문 비밀번호를 사용자에게 알려줘야 한다는 것이다. 즉, 비밀번호가 역변환 가능해야 한다는 것이다.
그런데 위에서 설명한 변환 방식은 역변환이 불가능하다. “x9$mK2@pLq4”라는 변환된 값을 갖고 원래 비밀번호 “password123”을 알아낼 방법이 없다. 이것이 바로 보안의 핵심이다.
비밀번호 찾기 기능을 제공하려면 역변환이 가능한 방식을 사용해야 한다. 그렇게 되면 DB가 털렸을 때 해커도 같은 방법으로 역변환할 수 있다. 우리가 위에서 논의한 보안 전략이 무용지물이 되는 것이다.
비밀번호 초기화는 비밀번호 찾기 기능보다 보안 측면에서 안전하다. 그 이유는 역변환 불가능한 방식을 그대로 유지할 수 있기 때문이다.
비밀번호 초기화 요청이 들어왔을 때 무작위 문자열을 새 비밀번호로 생성하고 사용자 메일로 해당 평문 비밀번호를 보낸다. 한편, 생성한 무작위 평문 비밀번호는 역변환 불가능한 방식으로 변환한 후 DB에 덧씌워 저장한다. 사용자는 새로 생성된 비밀번호를 갖고 로그인할 수 있다. 보안 전략을 유지할 수 있어 훨씬 안전하다.
한계는 있다. 각 비밀번호마다 고유한 랜덤 값이 있어 다른 비밀번호를 보호해준다 하지만, 해커가 시간을 들여 흔한 비밀번호들에 대한 변환 값 목록을 만들어 나간다면, 약한 비밀번호를 사용하는 사용자들은 결국 뚫릴 수 있다. 그럼에도 개발자는 정보를 보호하기 위해 편집증적으로 이런 저런 전략을 채택할 필요가 있고, 그렇기 때문에 비밀번호 찾기 기능은 채택되지 않은 것이다.
비밀번호 보안을 위해 아래 세 가지 전략을 채택했다. 비밀번호 찾기 기능을 위해서는 3번 전략을 포기해야 하는데, 포기함으로써 얻는 유저 경험의 증가분보다 보안 리스크의 증가분이 더 크다고 판단해서 비밀번호 찾기 기능은 도입하지 않는다.
하지만, 이 방법들로도 100% 안전하게 비밀번호를 지킬 수는 없다. 우리가 할 수 있는 것은 최악의 상황에도 해커가 정보를 알아내는 데 걸리는 시간을 늘리는 것뿐이다.
본문에서는 이해를 돕기 위해 “변환”이라는 쉬운 표현을 사용했지만, 기술적으로 정확한 용어는 다음과 같다:
비밀번호는 반드시 해시 함수로 저장해야 한다. 대표적인 비밀번호 해시 함수로는 bcrypt(레거시), scrypt, Argon2, Argon2id 등이 있으며, 이들은 의도적으로 느리게 설계되어 무차별 대입 공격을 방어한다.
본문에서 언급한 “각 비밀번호마다 고유한 랜덤 값”을 솔트(Salt) 라고 한다. 솔트는 비밀번호와 함께 해시 함수에 입력되는 랜덤 문자열로, 같은 비밀번호라도 서로 다른 해시값을 만들어낸다.
예를 들어:
솔트는 DB에 해시값과 함께 저장된다. 로그인 시에는 입력받은 비밀번호에 DB에 저장된 솔트를 붙여 해시한 후, DB의 해시값과 비교한다.
DB가 털려도 솔트가 있으면 공격자는 각 비밀번호마다 별도로 공격해야 하므로, 공격 난이도가 기하급수적으로 증가한다.
1. 무차별 대입 공격(Brute Force Attack)
가능한 모든 문자 조합을 시도하는 공격. “a”, “b”, “c”, … “aaa”, “aab” 식으로 모든 경우를 시도한다. 느린 해시 함수를 사용하면 효과적으로 방어할 수 있다.
2. 사전 공격(Dictionary Attack)
흔히 사용되는 비밀번호 목록(“password”, “123456”, “qwerty” 등)을 미리 준비해서 시도하는 공격. 약한 비밀번호는 순식간에 뚫린다.
3. 레인보우 테이블 공격(Rainbow Table Attack)
미리 계산해 둔 [평문: 해시값] 테이블을 사용하는 공격. 솔트가 없으면 한 번의 계산으로 여러 비밀번호를 동시에 공격할 수 있지만, 솔트가 있으면 각 비밀번호마다 별도의 테이블이 필요하므로 사실상 불가능해진다.
방법 2: 양방향 암호화 저장
하지만, 여기도 한계는 있다. DB가 털리고 해커가 암호화된 비밀번호를 갖고 있을 때, 가능한 모든 문자열을 가능한 모든 비밀 키를 이용해서 암호화한 뒤, 암호화된 비밀번호 중 하나와 일치하는지 확인해 보는 방식으로 비밀 키를 알아맞힐 수 있다.
라고 하였지만, 실제로는 충분히 강한 암호화 키(예: AES-256)를 사용한다면 무차별 대입 공격으로 키를 찾는 것은 현실적으로 불가능하다. 키 공간이 2^256으로 우주의 원자 수보다 많기 때문이다. 대신 키 관리의 취약점(키 저장 방식, 키 유출 등)이 더 현실적인 위험 요소가 되겠다.
전략들을 취사선택하는 느낌으로 글을 작성했지만, 이는 법적으로 무조건 지켜야 하는 의무사항이다.
「개인정보의 안전성 확보조치 기준」 제7조:
① 개인정보처리자는 비밀번호, 생체인식정보 등 인증정보를 저장 또는 정보통신망을 통하여 송ㆍ수신하는 경우에 이를 안전한 암호 알고리즘으로 암호화하여야 한다. 다만, 비밀번호를 저장하는 경우에는 복호화되지 아니하도록 일방향 암호화하여 저장하여야 한다.
② 개인정보처리자는 다음 각 호의 해당하는 이용자의 개인정보에 대해서는 안전한 암호 알고리즘으로 암호화하여 저장하여야 한다.
- 주민등록번호
- 여권번호
- 운전면허번호
- 외국인등록번호
- 신용카드번호
- 계좌번호
- 생체인식정보
…
안전한 암호 알고리즘은 충분히 느려서 무차별 대입 공격을 방지할 만한 검증된 알고리즘을 말한다. 비밀번호의 경우, 일방향 암호화(해싱)로 저장하라고 명시되어 있다.
POPO의 개인정보 처리방침의 12. 개인정보의 안전성 확보 조치 에도 관련 내용을 명시하고 있다:
- 개인정보의 암호화 이용자의 비밀번호는 단방향 암호화 되어 저장 및 관리되고 있어 본인만이 알 수 있으며, 암호화된 비밀번호를 탈취하더라도 평문으로 된 비밀번호를 알 수 없습니다.
추가로, 계좌번호도 암호화를 해야 한다. 이 때는 복호화 가능하게 저장할 수 있다. 이에 따라 POPO 앱에서 서비스하는 카풀 서비스에서는 사용자 편의를 위해 자주 쓰는 계좌번호를 암호화하여 저장하고, 정산 시 복호화해 사용자에게 알려주어 간편하게 불러올 수 있게 했다.