VillageSQL is a drop-in replacement for MySQL with extensions.
All examples on this page work on VillageSQL. Install Now →
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만큼 강한가요?
같은 반복 횟수에서는 bcrypt가 메모리 집약적(GPU에서 병렬화하기 어려움)이라 약간 우위에 있습니다. 높은 반복 횟수(100,000 이상)의 PBKDF2도 여전히 강력하고 널리 배포되어 있습니다. 그 차이는 적대적 시나리오에서 중요합니다; 둘 다 SHA2보다 훨씬 낫습니다.기존 SHA2 해시를 마이그레이션할 수 있나요?
예.needs_rehash 플래그를 추가하세요. 로그인에 성공하면 해시가 오래된 SHA2 형식인지 확인하고 crypt()로 다시 해싱하세요. 활성 사용자가 모두 로그인한 뒤에 오래된 해시를 제거하세요.
PostgreSQL의 기존 bcrypt 해시를 마이그레이션할 수 있나요?
직접은 안 됩니다.crypt()는 PBKDF2 형식이 아닌 솔트나 해시에 대해 NULL을 반환하므로, pgcrypto의 bcrypt 해시는 여기서 검증할 수 없습니다. 기존 열에 그대로 두고, 위의 SHA2 해시와 같은 방식으로 각 사용자의 다음 로그인 성공 시 다시 해싱하세요. PostgreSQL에서 MySQL로 마이그레이션 가이드에 전체 확장 매핑이 있습니다.
저장된 해시에 솔트가 포함되나요?
예.crypt()의 출력은 알고리즘, 반복 횟수, 솔트, 해시를 하나의 문자열에 인코딩합니다. 솔트를 따로 관리하지 않습니다.
문제 해결
함께 보기
- MySQL의 데이터 해싱 — 무결성 검사와 중복 제거를 위한 비밀번호 외 해싱
- MySQL의 대칭 암호화 — (해싱이 아닌) 암호화가 올바른 선택일 때
- MySQL의 랜덤 데이터 생성 — 암호학적으로 안전한 솔트와 토큰 생성