2010년 1월 22일
앞 글에 이어서, 여기부터는 사용자들이 안전한 암호를 선택하도록 유도하려면 어떻게 해야 할지 잡소리를 해 보자. 반쯤은 내 불평도 담겨 있다.
- 절대로, 절대로, 절대로 암호를 평문으로 저장하지 않는다. 또한 가능하면 최대한 느리면서 안전한 해시 함수랑 HMAC, 그리고 salt를 결합해서 사용하라. (이를테면 php에서는
hash_hmac함수를 쓰란 소리) 왜 이래야 하냐고?- 해시 함수를 쓰는 궁극적인 목적은 설령 공격자가 서버에 침입해 와도 많은 정보를 얻지 못 하게 하려 함이다. 한 사이트 암호 뚫렸다고 같은 암호 쓰는 다른 사이트에서도 암호가 줄줄이 뚫리는 상황이 웃기지 아니한가? 암호 찾기 기능은 응당 암호를 평문으로 알려주는 것이 아닌 임시 암호로 변경하고 알려 주는 시스템이 되어야 한다.
- 당연한 말이지만, 해시 함수가 안전하지 않으면 HMAC는 아무 효력도 발휘하지 못 한다. 최소한 CRC32 같은 건 피해라. MD5는 충돌은 발견되었지만 역변환은 여전히 어려우므로 아직까지 쓰는 건 나쁘지 않다.
- 두 해시 함수가 동등하게 안전할 경우 느린 쪽을 선택해야 공격에 걸리는 시간을 줄일 수 있다. 보통 안전한 해시 함수의 공격은 레인보우 테이블로 이루어지는데, 이 자료구조의 성능은 해시 함수의 속도에 비례한다.
- HMAC 없이 해시 함수를 쓰는 것은 요즘 세상에는 무모한 짓에 가깝다. MD5나 SHA-1 같은 유명한 해시 함수들에 대해서는 이미 짧은(~10바이트 정도) 문자열에 대한 해시값을 온라인으로 검색해 볼 수 있다. 기본적으로 같은 알고리즘을 쓰더라도 사이트 별로 다른 salt를 사용해야 이런 문제를 피할 수 있다.
- 그리고 자기 나름대로 암호화 방식 만들겠다고 삽 파지 말 것. 잘 알려진 알고리즘을 갖다가 쓰는 게 훨씬 안전하다. 코드소프트 꼴이 나고 싶은가?
- 암호 체크를 할 때는 가급적이면 안전한 채널(이를테면, HTTPS 연결)을 통해 한다.
- 그러니까 패킷 스니핑 따위에 암호가 노출되지는 않도록 노력하란 뜻이다. 물론 클라이언트 사이드에서 키로거 같은 게 돌고 있으면 뭐 어쩔 수 없다.
- 이게 여의치 않다면 challenge-response 인증을 대안으로 쓸 수 있다만 이 쪽은 고려할 게 많으므로 여유가 있으면 HTTPS가 훨씬 간편하고 안전하다. (예를 들어 앞에서 말한 HMAC을 사용할 경우 단순한 프로토콜을 사용할 수 없다.)
- 암호 넣는 칸의 길이를 제한하지 말라. 아무리 적어도 64바이트는 쓸 수 있게 하라.
- 최대 암호 길이를 제한하는 것은 기본적으로 사용자가 입력할 수 있는 암호의 종류를 크게 제한하는 것과 마찬가지이다! 어차피 해시 함수를 쓸 경우 암호의 길이와 관계 없이 해시된 값은 크기가 일정하므로 크기를 제한할 이유가 사실상 없다.
- 길이 제한이 하여튼 있다면, 길이를 넘어서 입력하려 할 경우 사용자에게 제대로 경고를 내는 인터페이스를 만들어야 한다. 그래야 다음에 설명할 불상사를 반쯤 예방할 수 있다.
- 또 하나 주의할 점은, 가입이나 암호 변경시 사용하는 칸의 길이 제한과 암호 넣는 칸의 길이 제한이 (만약 있다 하더라도) 같아야 한다는 것이다. 안 그러면 긴 암호 쓰려는 사람이 아무 경고도 없이 긴 암호 쓰다가 로그인이 안 되는 불상사가 발생할 수 있다.
- 암호에 문자 제한 좀 하지 마라!
- 여기에 한 가지 예외를 둔다면 유니코드 문자는 제한을 걸 수 있다. 왜냐하면 일부 브라우저에서는 입력이 되고 일부 브라우저에서는 안 되는 경우가 실제로 존재하기 때문에 (…) 하지만 그게 아니라면 어떤 제한도 두지 마라.
- 길이 제한 두고 무조건 특수문자를 쓰도록 강제하는 것보다 길이 제한을 푸는 게 차라리 낫다. 충분히 긴 암호는 짧고 특수문자 쓰는 암호보다 예측하기 어렵다. (실제로 나는 한때 열 여섯자리 숫자를 암호로 쓴 적이 있다. 여기에 대응하는 정보량은 53.2비트로 아홉글자 무작위 암호와 맞먹는다.)
- 문자를 강제로 제한하는 것보다 좀 더 세밀한 휴리스틱을 동원하여 이게 안전한지 아닌지를 산정할 것. 예를 들어서
!@#$%^&*()은 모두 특수문자로 이루어져 있음에도 불구하고 키보드에서 연속된 위치에 배치되어 있기 때문에 굉장히 예측 가능한 암호이다. 한편 앞에서 말한 열 여섯자리 숫자는 랜덤하게 만들어졌기 때문에 예측이 어렵다. 이게 당장은 어렵더라도 충분히 고민해 봐야 할 문제 아닐까?
