메인 콘텐츠로 건너뛰기

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
MySQL에는 MD5()와 SHA2()가 있습니다. 개발자들은 비밀번호를 해싱할 때 이 함수들에 손을 뻗습니다. 그것은 실수입니다 — 이 함수들은 빠르도록 설계되었는데, 그것이 바로 비밀번호 저장에는 정반대로 잘못된 특성입니다.

빠른 해시가 비밀번호에 잘못된 이유

MD5와 SHA-256 같은 암호화 해시 함수는 속도에 최적화되어 있습니다. 최신 GPU는 초당 수십억 개의 SHA-256 해시를 계산할 수 있습니다. 체크섬과 데이터 무결성에는 유용합니다. 비밀번호에서는, 해시된 비밀번호 테이블을 손에 넣은 공격자가 몇 시간 안에 무차별 대입으로 깰 수 있다는 뜻입니다. 비밀번호 해싱 알고리즘 — bcrypt, Argon2, PBKDF2 — 은 의도적으로 느립니다. 비밀번호 하나를 확인하는 데 수십 밀리초가 걸리도록 설계되어 있어, 로그인에는 문제가 없지만 무차별 대입 공격은 비현실적으로 만듭니다.

표준 답: 애플리케이션 코드에서 해싱

대부분의 MySQL 애플리케이션은 애플리케이션 계층에서 해싱하여 이것을 올바르게 처리합니다 — PHP, Python, Node.js, 또는 앱을 실행하는 어떤 언어에서든 bcrypt를 사용합니다. 데이터베이스는 해시된 값만 봅니다. 이 방식은 동작하지만 분리가 생깁니다: 해싱 로직은 애플리케이션 코드에 있고 데이터는 데이터베이스에 있습니다. 마이그레이션, 대량 작업, 데이터 가져오기는 올바르게 해싱하려면 애플리케이션을 거쳐야 하거나, 여러 코드베이스에 해싱 로직을 복제해야 합니다.

VillageSQL에서: SQL에서 직접 PBKDF2

VillageSQL의 vsql_crypto 확장은 crypt()와 gen_salt()를 추가합니다 — PostgreSQL의 pgcrypto를 본뜬 PBKDF2 기반 비밀번호 해싱 API입니다. 애플리케이션 코드를 거치지 않고 SQL에서 비밀번호를 해싱하고 검증할 수 있습니다.

비밀번호 저장하기

gen_salt는 PBKDF2 계열만 다룹니다 — pbkdf2-sha256과 pbkdf2-sha512, 그리고 더 짧은 별칭인 pbkdf2, sha256, sha512입니다. pgcrypto의 bf(bcrypt), md5, des 타입은 지원하지 않습니다: 그 이름에 대해 gen_salt는 NULL을 반환합니다.

비밀번호 검증하기

crypt()는 저장된 해시에서 솔트와 매개변수를 자동으로 추출합니다 — 저장된 해시를 두 번째 인수로 넘기면 됩니다:

반복 횟수 선택하기

반복 횟수는 해시 계산에 걸리는 시간을 제어합니다. 반복이 많을수록 = 느릴수록 = 무차별 대입이 어렵지만, 로그인도 느려집니다. 10,000에서 시작해 하드웨어가 허용하는 만큼 늘리세요. 로그인 엔드포인트는 100ms를 견딜 수 있지만, 수십억 번 추측을 시도하는 공격자는 견딜 수 없습니다.

SQL과 애플리케이션 코드 중 어디서 해싱할지

SQL에서 해싱하는 것이 항상 올바른 선택은 아닙니다. 다음과 같을 때 사용하세요:
  • 앱을 거치는 것이 현실적이지 않은 대량 삽입이나 마이그레이션을 수행할 때
  • 해싱 로직을 데이터베이스 계층에 집중시키고 싶을 때
  • SQL 기반 관리 도구나 시드 스크립트를 만들 때
다음과 같을 때는 애플리케이션 코드에서 해싱하세요:
  • 애플리케이션 프레임워크에 이미 잘 검증된 bcrypt/Argon2 라이브러리가 있을 때
  • 암호화 생태계가 성숙한 언어를 사용할 때(대부분 그렇습니다)
  • 스키마 마이그레이션 없이 해싱 알고리즘을 교체하고 싶을 때
두 접근 방식 모두 안전한 해시를 만듭니다. 핵심은 느린 알고리즘 — PBKDF2, bcrypt, 또는 Argon2 — 을 사용하는 것이지, 날것의 SHA2가 아닙니다.

자주 묻는 질문

PBKDF2는 bcrypt만큼 강한가요?

같은 반복 횟수에서는 bcrypt가 메모리 집약적(GPU에서 병렬화하기 어려움)이라 약간 우위에 있습니다. 높은 반복 횟수(100,000 이상)의 PBKDF2도 여전히 강력하고 널리 배포되어 있습니다. 그 차이는 적대적 시나리오에서 중요합니다; 둘 다 SHA2보다 훨씬 낫습니다.

기존 SHA2 해시를 마이그레이션할 수 있나요?

예. needs_rehash 플래그를 추가하세요. 로그인에 성공하면 해시가 오래된 SHA2 형식인지 확인하고 crypt()로 다시 해싱하세요. 활성 사용자가 모두 로그인한 뒤에 오래된 해시를 제거하세요.

PostgreSQL의 기존 bcrypt 해시를 마이그레이션할 수 있나요?

직접은 안 됩니다. crypt()는 PBKDF2 형식이 아닌 솔트나 해시에 대해 NULL을 반환하므로, pgcrypto의 bcrypt 해시는 여기서 검증할 수 없습니다. 기존 열에 그대로 두고, 위의 SHA2 해시와 같은 방식으로 각 사용자의 다음 로그인 성공 시 다시 해싱하세요. PostgreSQL에서 MySQL로 마이그레이션 가이드에 전체 확장 매핑이 있습니다.

저장된 해시에 솔트가 포함되나요?

예. crypt()의 출력은 알고리즘, 반복 횟수, 솔트, 해시를 하나의 문자열에 인코딩합니다. 솔트를 따로 관리하지 않습니다.

문제 해결

함께 보기