비밀번호 해싱의 변천사
비밀번호 해싱 알고리즘 선택에 대한 조언을 들어 보셨을 것입니다. 그러나 왜 추천하는지에 대해 생각해 보셨나요? 이 글에서는 비밀번호 해싱 알고리즘의 발전과 그 이유에 대해 살펴보겠습니다.
소개
비밀번호 해싱이란, 비밀번호에서 해시 값으로 계산하는 과정을 말합니다. 해시 값은 일반적으로 데이터베이스에 저장되며, 로그인 과정에서 사용자가 입력한 비밀번호의 해시 값이 계산되고 데이터베이스에 저장된 해시 값과 비교됩니다. 일치하면 사용자는 인증됩니다.
비밀번호 해싱 알고리즘의 발전에 대해 들어가기 전에 왜 이것이 필요한지 이해하는 것이 중요합니다.
평문 비밀번호: 주요 보안 위험
웹사이트에서 계정을 등록한 사용자라고 상상해봅시다. 어느 날, 웹사이트가 해킹당하고 데이터베이스가 유출되었습니다. 웹사이트가 평문으로 비밀번호를 저장하면, 해커는 직접 비밀번호에 접근할 수 있습니다. 많은 사람들이 다양한 웹사이트에서 비밀번호를 재사용하기 때문에, 해커는 이 비밀번호를 사용해 다른 계정에 무단 접근할 수 있습니다. 이메일 계정에서 같은 또는 비슷한 비밀번호를 사용하면 상황이 더욱 악화됩니다. 왜냐하면, 해커는 비밀번호를 재설정하고 관련 계정을 모두 점령할 수 있기 때문입니다.
데이터 유출이 없더라도, 큰 팀에서는 데이터베이스에 접근할 수 있는 사람이 비밀번호를 볼 수 있습니다. 다른 정보와 비교했을 때, 비밀번호는 매우 민감하며, 당신은 반드시 누구도 이에 접근하지 않도록 원할 것입니다.
해싱 없이 비밀번호를 저장하는 것은 초보적인 실수입니다. 불행하게도, "비밀번호 유출 평문"을 검색하면 Facebook, DailyQuiz, GoDaddy와 같이 주요 기업들이 모두 평문으로 비밀번호 유출을 경험했다는 것을 알 수 있습니다. 아마도 많은 다른 회사들이 같은 실수를 저지른 것 같습니다.
인코딩 vs. 암호화 vs. 해싱
이 세 가지 용어는 종종 혼동되지만, 그것들은 다른 개념입니다.
인코딩
비밀번호 저장을 위해 제외해야 할 첫 번째 것이 인코딩입니다. 예를 들어, Base64는 이진 데이터를 문자열로 변환하는 인코딩 알고리즘입니다:
인코딩 알고리즘을 알면 누구나 인코드된 문자열을 디코드하고 원래의 데이터를 검색할 수 있습니다:
해커들에게 대부분의 인코딩 알고리즘은 평문과 같습니다.
암호화
해싱이 널리 사용되기 전에 암호화가 비밀번호를 저장하는 데 사용되었습니다. 예를 들어, AES와 같은 것입니다. 암호화는 키(또는 키 쌍) 를 사용하여 데이터를 암호화하고 복호화합니다.
암호화의 문제점은 "복호화"이라는 용어에서 명백합니다. 암호화는 역전시킬 수 있어, 해커가 키를 얻으면 비밀번호를 복호화하고 평문 비밀번호를 검색할 수 있습니다.
해싱
해싱, 인코딩, 암호화 사이의 주요 차이점은 해싱이 불가역이라는 것입니다. 비밀번호가 한번 해싱되면 원래의 형태로 복호화할 수 없습니다.
웹사이트 소유자로서, 사용자가 올바른 비밀번호를 사용하여 로그인할 수 있다면 실제 비밀번호를 알 필요는 없습니다. 등록 과정은 다음과 같이 간단하게 단순화될 수 있습니다:
- 사용자가 비밀번호를 입력합니다.
- 서비스는 해싱 알고리즘을 사용하여 비밀번호의 해시 값을 계산합니다.
- 서비스는 데이터베이스에 해시 값을 저장합니다.
사용자가 로그인할 때, 과정은 다음과 같습니다:
- 사용자가 비밀번호를 입력합니다.
- 서비스는 같은 해싱 알고리즘을 사용하여 비밀번호의 해시 값을 계산합니다.
- 서비스는 해시 값과 데이터베이스에 저장된 해시 값을 비교합니다.
- 해시 값이 일치하면 사용자가 인증됩니다.
두 프로세스 모두 평문에서 비밀번호를 저장하지 않고 해싱이 불가역이기 때문에, even if 데이터베이스가 손상되면, 해커는 무작위 문자열로 보이는 해시 값을 얻을 수만 있을 것입니다.
해싱 알고리즘 스타터 팩
해싱이 비밀번호 저장을 위한 완벽한 해결책처럼 보일 수 있지만 그렇게 단순하지는 않습니다. 왜 그런지 이해하기 위해, 비밀번호 해싱 알고리즘의 발전을 살펴봅시다.
MD5
1992년에 Ron Rivest가 MD5 알고리즘을 설계했습니다. 이것은 모든 데이터에서 128비트 해시 값을 계산할 수 있는 메시지 다이제스트 알고리즘입니다. MD5는 비밀번호 해싱을 포함하여 다양한 분야에서 널리 사용되었습니다. 예를 들어, "123456"의 MD5 해시 값은 다음과 같습니다:
앞서 언급했듯이, 해시 값은 무작위 문자열로 보이며 불가역입니다. 또한, MD5는 빠르고 구현하기 쉬워 가장 인기 있는 비밀번호 해싱 알고리즘이 되었습니다.
그러나, MD5의 장점은 또한 비밀번호 해싱에서의 약점입니다. 그것의 속도는 무작위 공격에 취약하게 만듭니다. 해커가 일반적인 비밀번호와 당신의 개인 정보 목록을 보유하고 있다면, 그들은 각 조합의 MD5 해시 값을 계산하고 데이터베이스의 해시 값과 비교할 수 있습니다. 예를 들어, 그들은 당신의 생일과 당신의 이름 또는 당신의 애완 동물의 이름을 결합할 수 있습니다.
현재, 컴퓨터는 이전보다 훨씬 더 강력하여, MD5 비밀번호 해시를 무작위로 강제하기 쉽습니다.
SHA 계열
그렇다면, 더 긴 해시 값을 생성하는 다른 알고리즘은 어떠한가요? SHA 계열이 좋은 선택처럼 보입니다. SHA-1은 160비트 해시 값을 생성하는 해싱 알고리즘이며, SHA-2는 224비트, 256비트, 384비트, 512비트 길이의 해시 값을 생성하는 해싱 알고리즘 계열입니다. "123456"의 SHA-256 해시 값을 살펴봅시다:
SHA-256 해시 값은 MD5보다 훨씬 긴데, 이것도 불가역입니다. 그러나, 또 하나의 문제가 있습니다: 이미 해시 값을 알고 있고, 데이터베이스에서 정확히 같은 해시 값을 보면, 비밀번호가 "123456"인 것을 알 수 있습니다. 해커는 일반적인 비밀번호와 그에 해당하는 해시 값을 목록에 넣을 수 있고, 이것을 데이터베이스의 해시 값과 비교할 수 있습니다. 이 목록을 레인보우 테이블이라고 합니다.
Salt
레인보우 테이블 공격을 완화하기 위해, salt라는 개념이 도입되었습니다. Salt는 해싱하기 전에 비밀번호에 추가되는 임의의 문자열입니다. 예를 들어, salt가 "salt"이고, salt와 함께 비밀번호 "123456"을 SHA-256으로 해싱하려면, 단순히 다음을 수행하는 대신:
다음을 실행하면 됩니다:
보시다시피, 결과는 salt 없이 해싱하는 것과 전혀 다릅니다. 일반적으로, 각 사용자는 등록시 임의의 salt를 부여받으며, 이것은 해시 값과 함께 데이터베이스에 저장됩니다. 로그인 과정에서는, salt가 입력된 비밀번호의 해시 값을 계산하는 데 사용되고, 이것은 저장된 해시 값과 비교됩니다.
반복
Salt가 추가되었음에도 불구하고, 하드웨어가 강력해짐에 따라 해시 값은 여전히 무작위 공격에 취약합니다. 이것을 더 어렵게 만들려면, 반복(즉, 해싱 알고리즘을 여러 번 실행)을 도입할 수 있습니다. 예를 들어, 다음을 사용하는 대신:
당신은 다음을 사용할 수 있습니다:
반복 횟수를 늘리면 무작위 공격이 더 어려워집니다. 그러나, 이것은 또한 로그인 과정에 영향을 미치며, 로그인 과정이 더 느려집니다. 따라서, 보안과 성능 사이의 균형을 유지해야 합니다.
하프타임 휴식
좋은 비밀번호 해싱 알고리즘의 특징들을 요약 잠시 쉬고 봅시다:
- 능역성 (원상 복귀 저항력)
- 무작위 공격에 대한 저항력
- 레인보우 테이블 공격에 대한 저항력
알아두셨겠지만, 이 모든 요구사항을 만족시키려면 Salt와 반복이 필요합니다. 문제는 MD5와 SHA 계열이 비밀번호 해싱을 위해 디자인되지 않았다는 것입니다; 그들은 무결성 검사(또는 "메시지 다이제스트")를 위해 널리 사용됩니다. 따라서, 각 웹사이트는 Salt와 반복에 대한 자체 구현을 가질 수 있어, 표준화 및 이전이 어렵습니다.
비밀번호 해싱 알고리즘
이 문제를 해결하기 위해, 일부 해싱 알고리즘은 비밀번호 해싱을 위해 특별히 디자인되었습니다. 이 중 일부를 살펴봅시다.
bcrypt
bcrypt는 Niels Provos와 David Mazières가 설계한 비밀번호 해싱 알고리즘입니다. 이것은 많은 프로그래밍 언어에서 널리 사용됩니다. bcrypt 해시 값의 예를 들면 다음과 같습니다:
이것은 또 다른 무작위 문자열처럼 보이지만, 추가 정보를 포함하고 있습니다. 다음과 같이 분해해봅시다:
- 첫 번째 섹션
$2y
는 알고리즘을 나타내며, 이는2y
입니다. - 두 번째 섹션
$12
은 반복 횟수를 나타내며, 이는12
입니다. 이는 해싱 알고리즘이 212=4096번 실행됨을 의미합니다(반복). - 세 번째 섹션
wNt7lt/xf8wRJgPU7kK2ju
은 salt입니다. - 마지막 섹션
GrirhHK4gdb0NiCRdsSoAxqQoNbiluu
은 해시 값입니다.
bcrypt에는 약간의 제한이 있습니다:
- 비밀번호의 최대 길이는 72바이트입니다.
- Salt는 16바이트로 제한됩니다.
- 해시 값은 184비트로 제한됩니다.
Argon2
기존 비밀번호 해싱 알고리즘에 대한 논란과 제한 사항을 고려할 때, 2015년에 비밀번호 해싱 대회가 열렸습니다. 세부사항은 생략하고, 우승자인 Argon2에 집중하겠습니다.
Argon2는 Alex Biryukov, Daniel Dinu, Dmitry Khovratovich가 설계한 비밀번호 해싱 알고리즘입니다. 이것은 몇 가지 새로운 개념을 도입했습니다:
- 메모리하드: 알고리즘은 병렬화하기 어렵게 설계되어, GPU를 사용한 무작위 강제가 어렵습니다.
- 시간하드: 알고리즘은 최적화하기 어렵게 설계되어, ASICs(특정 용도 집적 회로)를 사용한 무작위 강제가 어렵습니다.
- 사이드 채널 저항력: 알고리즘이 타이밍 공격 등의 사이드 채널 공격에 대해 저항하도록 설계되었습니다.
Argon2에는 두 가지 주요 버전, Argon2i와 Argon2d가 있습니다. Argon2i는 사이드 채널 공격에 대해 가장 안전하며, Argon2d는 GPU 크래킹 공격에 대한 가장 높은 저항력을 제공합니다.
-- Argon2
Argon2 해시 값의 예를 들면 다음과 같습니다:
이것을 분해해 보겠습니다:
- 첫 번째 섹션
$argon2i
는 알고리즘을 나타냅니다. 이는argon2i
입니다. - 두 번째 섹션
$v=19
은 버전을 나타냅니다. 이는19
입니다. - 세 번째 섹션
$m=16,t=2,p=1
은 메모리 비용, 시간 비용, 병렬도를 나타냅니다. 이것들은 각각16
,2
,1
입니다. - 네 번째 섹션
$YTZ5ZnpXRWN5SlpjMHBDRQ
은 salt입니다. - 마지막 섹션
$12oUmJ6xV5bIadzZHkuLTg
은 해시 값입니다.
Argon2에서 비밀번호의 최대 길이는 232-1 바이트, salt는 232-1 바이트로 제한되며, 해시 값은 232-1 바이트로 제한됩니다. 이는 대부분의 시나리오에서 충분할 것입니다.
Argon2는 이제 많은 프로그래밍 언어, 예를 들어 Node.js용 node-argon2 및 Python용 argon2-cffi에서 사용할 수 있습니다.
결론
여러 해 동안, 비밀번호 해싱 알고리즘은 상당한 발전을 거쳤습니다. 인터넷을 더 안전한 장소로 만들기 위해 수십 년 동안 노력한 보안 커뮤니티에게 감사의 말을 전합니다. 그들의 기여 덕분에, 개발자들은 비밀번호 해싱의 보안에 대해 걱정하지 않고 더 나은 서비스를 만드는 데 더 많은 주의를 기울일 수 있습니다. 시스템에서 100%의 보안을 달성하는 것은 어려울 수 있지만, 우리는 다양한 전략을 사용하여 관련 위험을 최소화할 수 있습니다.
인증 및 권한 부여를 구현하는 번거로움을 피하고 싶다면, Logto를 무료로 시도해 보세요. 우리는 안전한(우리는 Argon2를 사용합니다!), 신뢰할 수 있는, 확장 가능한 솔루션을 제공하며, 당신이 제품을 만드는 데 집중할 수 있도록 도와줍니다.