제대로 만든 사이트는 당신의 비밀번호를 모른다. 알고 싶지 않아서가 아니라, 알 수가 없어서다.
말장난처럼 들린다. 비밀번호를 입력할 때마다 시스템은 당신을 통과시키고, 통과시키려면 무언가를 확인해야 하니까. 하지만 확인하는 것과 아는 것은 다르다. 서버는 당신의 비밀번호에서 계산한 지문을 보관한다. 당신이 입력하면 다시 지문을 계산해서 비교한다. 일치하면 통과. 그 어느 순간에도 비밀번호 자체가 어딘가에 적혀 있을 필요는 없다.
여기서 진지한 서비스와 그렇지 않은 서비스를 가르는 가장 확실한 신호가 나온다. 비밀번호를 잊었다고 했더니 메일로 그대로 보내준다면, 그들은 그것을 저장하고 있었다는 뜻이다. 그리고 저장된 것은 언젠가 반드시 다른 누군가의 손에 들어간다.
1979년 저장하지 않는다는 발상, 그리고 느리게 만든다는 발상
1979년, Robert Morris와 Ken Thompson은 Communications of the ACM에 〈Password Security: A Case History〉를 발표하며 유닉스에서 자신들이 한 일을 설명했다. 배운 것을 정리한 기록인데, 오늘날 우리가 당연하게 여기는 두 가지 발상이 여기서 자리를 잡았다.
첫 번째는 그들이 발명한 것이 아니다. 비밀번호 대신 해시를 저장하는 일은 그전에도 있었다. 다만 누구보다 잘 설명했을 뿐이다. 비밀번호를 저장하지 말고, 그 해시를 저장하라. 비밀번호를 아무렇게나 생긴 값으로 바꿔놓고 되돌릴 수는 없는 함수. 해시에서 비밀번호로 돌아가는 길은 없다.
두 번째는 확실히 그들의 것이다. 솔트. 사용자마다 다른 무작위 값을 비밀번호에 섞은 다음 해시를 계산한다. 솔트가 없으면 같은 비밀번호를 쓰는 두 사람의 해시가 같아진다. 유출된 파일이 같은 값 찾기 놀이가 되어버리는 것이다. 더 나쁜 건, 누군가 거대한 표를 딱 한 번 미리 계산해두고 세상의 모든 데이터베이스에 써먹을 수 있다는 점이다. 솔트는 당신의 비밀번호를 더 알아맞히기 어렵게 만들지 않는다. 알아맞혀도 다른 누구에게는 쓸모가 없게 만든다.
그런데 같은 논문에 세 번째 발상이 있다. 훨씬 덜 인용되지만, 여기서는 이것이 핵심이다. 그들은 함수를 일부러 느리게 만들었다. 실수가 아니라 의도였다. 비밀번호 하나를 암호화하는 데 기술적으로 필요한 것보다 훨씬 많은 비용이 들게 했다. 아침에 한 번 들어오는 사람에게는 아무 상관 없는 비용이지만, 수백만 개의 후보를 대입하는 사람에게는 그렇지 않기 때문이다.
거의 오십 년 전 문장 하나에 이 이야기의 전부가 들어 있다.
느림이 유일하게 정직한 방어인 이유
컴퓨터 보안의 방어책은 대부분 비대칭이다. 방어하는 쪽에는 싸고 공격하는 쪽에는 비싸다. 암호화는 값싸고, 키 없이 복호화하는 것은 불가능에 가깝다. 방어자는 구조적으로 유리한 패를 쥐고 시작한다.
비밀번호 저장은 그렇지 않다. 여기서는 방어자와 공격자가 정확히 같은 함수를 실행한다. 당신은 맞혔는지 확인하려고 해시를 계산하고, 그는 맞혔는지 확인하려고 해시를 계산한다. 보안 전체에서 양쪽이 똑같은 다리로 뛰는 유일한 경주다.
상대보다 빨리 뛸 수 없다면 남은 수는 하나뿐이다. 양쪽 모두에게 트랙 사용료를 올리는 것. 당신은 로그인 한 번에 그 비용을 낸다. 그는 시도 한 번마다 낸다. 그에게 필요한 시도 횟수는 당신보다 몇 자릿수나 많으므로, 똑같은 청구서가 두 사람에게 비교도 안 되게 다르게 꽂힌다. 비밀번호 해시가 하는 일이 바로 이것이다.
그래서 다른 용도로는 훌륭한 빠른 함수들, 예를 들어 SHA-256 같은 것은 여기서 최악의 선택이다. 빠르니까. 빠른 것이야말로 우리가 원하지 않는 바로 그것이다.
1999년 bcrypt, 그리고 조절 가능한 부품이 된 비용
느린 함수의 문제는 하드웨어가 가만히 있지 않는다는 것이다. 1979년에 느렸던 것은 얼마 뒤 순식간이 된다. 어느 십 년의 컴퓨터에 맞춰 조율된 방어는 아무도 손대지 않아도 저절로 낡는다.
1999년, Niels Provos와 David Mazières가 USENIX Annual Technical Conference에서 bcrypt를 발표했다. 제목이 이미 모든 것을 말한다. A Future-Adaptable Password Scheme.
발상은 이렇다. 비용을 함수 안이 아니라 파라미터에 두자. bcrypt에는 당신이 정하는 비용 인자가 있고, 그 값은 해시와 함께 저장된다. 하드웨어가 훨씬 빨라지면 숫자를 올린다. 그러면 새 비밀번호들은 다시 마땅한 만큼의 비용을 치른다. 알고리즘을 갈아엎을 필요가 없다.
자기 자신이 언젠가 낡을 것을 전제하고, 그에 대비해 설계된 물건이다. 그런 소프트웨어는 정말 드물다.
2009년 scrypt, 그리고 시간만으로는 부족하다는 발견
bcrypt는 계산 시간을 비싸게 만들었고, 잘 작동했다. 공격자가 방어자와 같은 종류의 기계를 쓰지 않게 되기 전까지는.
서버에는 범용의 빠른 코어가 몇 개 있다. GPU에는 작은 코어가 어마어마하게 많이 있고 전부 같은 일을 병렬로 한다. ASIC은 한 걸음 더 나간다. 단 하나의 연산을 실행하려고 만든 실리콘이다. 당신의 방어가 “이 연산은 X만큼 비싸다”에 기대고 있다면, 누군가는 X를 한꺼번에 잔뜩 처리하는 하드웨어를 만들 수 있다. 방어자는 여전히 서버 한 대다.
2009년, Colin Percival은 다른 논거를 들고 scrypt를 발표했다. Stronger Key Derivation via Sequential Memory-Hard Functions. 비용이 계산에만 있으면 병렬화된다. 하지만 메모리는 공짜로 주어지지 않는다. 함수가 많은 RAM을 쓰게 하고, 우회할 수 없는 방식으로 그 RAM을 훑고 다니게 하라. 그러면 갑자기 공격자는 코어를 늘릴 수가 없다. 코어마다 자기 몫의 메모리가 필요해지기 때문이다. 그리고 GPU의 코어들은 힘은 넘치지만 하나당 돌아가는 RAM은 형편없이 적다.
Percival은 연산을 비싸게 만들지 않았다. 그것을 대량으로 반복하는 데 필요한 하드웨어를 비싸게 만들었다. 층위가 달라진 것이다. 공격자의 시계가 아니라 공격자의 예산을 친다.
2015년 Argon2, 그리고 논쟁의 끝
그럴듯한 선택지가 여럿이라는 사실은 고르는 데 아무 도움이 되지 않았다. 그래서 암호학 커뮤니티는 무언가를 매듭짓고 싶을 때 늘 하는 일을 했다. 공개 경연이다. Password Hashing Competition은 후보들을 받아 몇 년에 걸쳐 공개 분석에 부쳤고, 2015년에 Argon2를 우승자로 선포했다.
Argon2는 두 교훈을 모두 물려받는다. 시간 비용과 메모리 비용, 둘을 따로 조절할 수 있고 스레드 수까지 있다. 마법이 아니다. 1979년과 1999년과 2009년을 가지런히 종합한 것이고, 그것을 깨뜨리는 일이 직업인 사람들의 검증을 거쳤다.
이것이 당신에게 뜻하는 바
이 중 거의 아무것도 당신 손에 달려 있지 않다. 이 점은 분명히 말해두는 편이 좋겠다. 해시는 사이트가 고르지, 당신이 고르지 않는다. 세상에서 가장 좋은 비밀번호를 쓰고도 그것이 엑셀 파일에 평문으로 저장될 수 있다.
하지만 등식의 반대편은 당신에게 달려 있다. 솔트도, 비용도, 메모리도, 이 건물 전체가 결국 공격자의 시도 한 번의 값을 올리려고 존재한다. 그가 몇 번 시도해야 하는지를 정하는 것은 당신의 비밀번호다. 사전에 실려 있는 단어라면 어떤 느린 함수도 당신을 구해주지 못한다. 맨 앞줄에서 무너진다. 길고 무엇과도 닮지 않았다면, 그 시도당 가격에 주어진 시간 안에 도저히 담기지 않는 횟수가 곱해진다.
그래서 우리 생성기는 백분율이 아니라 엔트로피 비트를 재고, 그래서 검사기는 기호 개수를 세기 전에 사전에서 당신의 비밀번호를 먼저 찾는다. 값은 그들이 매기고, 횟수는 당신이 매긴다. 어느 한쪽도 다른 쪽 없이는 작동하지 않는다.
그리고 그렇다. 여전히 늘 똑같은 질문이다. 다만 이제는 반대편에서 누군가가 초당 수없이 많은 횟수로 그 답을 시도하고 있고, 우리는 1979년부터 그 시도 하나하나에 돈이 들게 만들려고 애쓰고 있을 뿐이다.
출처: R. Morris and K. Thompson, “Password Security: A Case History”, Communications of the ACM, 1979 · N. Provos and D. Mazières, “A Future-Adaptable Password Scheme”, USENIX Annual Technical Conference, 1999 (bcrypt) · C. Percival, “Stronger Key Derivation via Sequential Memory-Hard Functions”, 2009 (scrypt) · Password Hashing Competition, 우승 Argon2, 2015.