MySQL과 PHP 환경에서의 비밀번호 해시 함수 취약점 분석 및 보안 강화 가이드
페이지 정보

본문
그누보드 운영자 및 개발자를 위해 MySQL 및 PHP 환경에서 사용되는 비밀번호 해시 함수의 취약점을 분석하고, 브루트포스 공격을 방어하기 위한 다중 해싱 및 토큰 키 활용 등 구체적인 보안 강화 방법을 제공합니다.
MySQL의 password()나 PHP의 md5(), crypt()와 같은 단방향 해시 함수가 무차별 대입(Brute Force) 공격에 취약한 이유는 다음과 같습니다.
1. 고정된 해시 패턴과 구조적 한계
단방향 해시 함수는 동일한 입력값에 대해 항상 동일한 결과값을 반환합니다. 이로 인해 데이터베이스(DB)가 외부로 노출될 경우, 공격자는 저장된 해시값의 패턴을 분석하여 해당 함수가 어떤 알고리즘(예: MD5)으로 생성되었는지 쉽게 파악할 수 있습니다.
2. 무차별 대입(Brute Force) 공격의 원리
공격자는 유출된 해시값을 바탕으로 가능한 모든 문자열 조합을 생성하여 동일한 해시 함수에 대입해 봅니다.
- 원문 유추: 공격자가 생성한 문자열의 해시값이 DB에 저장된 해시값과 일치하는 순간, 원래의 비밀번호를 즉시 알아낼 수 있습니다.
- 컴퓨팅 성능의 활용: 현대의 컴퓨터 성능은 매우 뛰어나며, 병렬 처리나 분할 연산을 통해 수많은 경우의 수를 단시간에 탐색할 수 있습니다. 따라서 단순한 해시 함수만으로는 비밀번호의 원문을 보호하는 데 한계가 있습니다.
DB 노출 시 단방향 해시 비밀번호 취약점 검증 과정
데이터베이스가 외부로 노출되었을 때, md5(), crypt(), password() 같은 단방향 해시 함수로 암호화된 비밀번호가 브루트포스(무차별 대입) 공격에 어떻게 뚫리게 되는지는 소스 코드 관점의 반복문(Loop)을 통해 확인할 수 있습니다.
1. MD5 해시 취약점 검증 소스 코드 관찰
사용자가 비밀번호를 특정 값으로 입력하여 md5() 함수로 해시화한 결과가 DB에 저장되어 있을 때, 공격자는 가능한 모든 문자열 조합을 반복문으로 대입하여 원래 비밀번호를 유추할 수 있습니다.
for( $pw = 0; $pw < 99999; $pw ++ ) {
if( '640258597cbc50037072712f964cf5d8' == md5( $pw ) ) {
echo "find ok: $pw ";
break;
}
}
// 출력 -> find ok: 3204
위와 같이 숫자 범위 내에서 순차적으로 md5() 값을 비교하면 원본 비밀번호인 3204가 쉽게 노출됩니다.
2. Crypt 함수 취약점 검증 소스 코드 관찰
crypt() 함수 역시 두 번째 인자인 salt 값에 따라 결과가 달라지지만, 저장된 해시 패턴을 기반으로 동일한 방식으로 반복문을 실행하면 원문을 찾아낼 수 있습니다.
for( $i = 0; $i < 99999; $i ++ ) {
if( '12FP8QJo.OCVg' == crypt( $i, '12FP8QJo.OCVg' ) ) {
echo " find ok: $i ";
break;
}
}
// 출력 -> find ok: 20930
숫자뿐만 아니라 문자열을 섞은 조합($pw = $chk.chr($i);) 형태로 확장하여 자동 대입하면, 컴퓨터 성능과 병렬 처리 능력을 통해 단기간에 원본 문자열을 복구할 수 있게 됩니다.
3. MySQL password() 함수 취약점 검증 소스 코드 관찰
MySQL 내부의 password() 함수로 암호화된 값 역시 데이터베이스 쿼리를 이용한 반복문을 통해 원문 숫자를 대입하여 확인할 수 있습니다.
for( $i = 0; $i < 99999; $i ++ ) {
$temp = mysql_fetch_array( mysql_query ( "select password('$i')" ) );
if( '4ed0bdda4ee8f6a5' == $temp[0] ) {
echo " find ok: $i ";
break;
}
}
// 출력 -> find ok: 50267
이와 같이 단일 해시 함수에만 의존하는 경우 DB가 노출되었을 때 무차별 대입 공격을 방어하기 어렵습니다.
다중 해시(이중 해싱) 적용 방법과 보안 강화
단일 해시 함수는 무차별 대입(Brute Force) 공격에 의해 원문을 쉽게 유추당할 수 있는 취약점이 있습니다. 이를 보완하기 위한 가장 쉬운 방법 중 하나는 암호화 함수 1개에만 의존하지 않고 여러 함수를 거쳐 2중 3중으로 조합하여 돌리는 것입니다.
다중 해시 설계 원리
- 모듈 유추 방지: 비밀번호가 어떤 암호화 모듈로 해시되었는지 해커가 쉽게 파악하지 못하게 만듭니다.
- 함수 중첩: 평문에 단일 해시 함수를 적용하는 대신, 여러 해시 함수나 알고리즘을 연속으로 거치게 하여 최종 해시값을 생성합니다.
추가 보안 대책 병행
다중 해시와 함께 다음과 같은 보안 설계를 조합하면 더욱 안전한 비밀번호 처리가 가능합니다.
- 서버 고유 토큰 키 조합: 외부에 노출되지 않는 서버만의 고유한 토큰 키(값)를 비밀번호에 조합하여 저장합니다. 키값은 DB에 저장하지 않고 접근 권한이 설정된 파일 등으로 관리합니다.
- 세션/쿠키 기반 1회용 키값 적용: 클라이언트와 통신할 때 접속 시마다 달라지는 유니크한 키값을 부여하여 주고받습니다. 라이프타임을 0으로 준 1회용 쿠키나 세션을 활용해 페이지 메모리 상에서만 연산하고 사라지게 처리합니다.
서버 고유 토큰 키 조합 방식
DB가 유출되더라도 서버 파일 접근 권한이 없다면 원본 비밀번호를 유추할 수 없도록 만드는 고유 키 조합(페퍼 또는 서버 고유 토큰 키) 전략의 구체적인 방식은 다음과 같다.
1. 서버 고유 토큰 키(키값) 설정 및 저장
- 외부에 노출되지 않는 서버만의 고유한 토큰 키(또는 페퍼)를 생성합니다.
- 해당 키값은 절대 DB에 저장하지 않으며, 서버 내부 파일 접근 권한이나 환경변수(
.env등)를 통해서만 접근할 수 있도록 설정해야 합니다.
2. 비밀번호 저장 시 키 조합 방식
- 사용자가 비밀번호(예:
1234)를 입력했을 때, 서버 측에서 고유의 키값을 더하거나 조합하여 저장합니다. - 예시: 서버 고유 키값
1011과 비밀번호1234를 조합(1011+1234)하여 연산된 결과값(2245또는 이를 거친 해시 결과)을 DB에 저장합니다. - 로그인 인증을 수행할 때도 동일한 서버 고유 키값을 불러와서 인증 과정을 거칩니다.
3. 주의 사항
- 서버 자체가 해킹당하기 전에는 외부에서 키값을 볼 수 없도록 파일의 실행 권한과 접근 권한을 엄격히 설정해야 합니다.
클라이언트 통신 시 세션 및 1회용 키값 적용 방법
네트워크 감시(스니핑) 상황에서 비밀번호가 그대로 노출되거나 해시된 문자열이 재사용되는 것을 방어하기 위해, 클라이언트와 서버 간 통신 시 접속할 때마다 달라지는 유니크한 키값을 부여하여 주고받을 수 있습니다.
1. 키값 생성 및 전달
- 서버 측에서 접속 시마다 틀린 유니크한 키값을 생성합니다. 예시로
rand(1000, 9000)과 같은 난수를 활용할 수 있습니다. - 생성된 키값은 클라이언트 페이지에 고정값으로 노출되어 임시 인터넷 파일 등에 남지 않도록 주의해야 합니다. 이를 위해
lifetime을0으로 설정한 1회용 쿠키나 세션에 담아 사용합니다. 예컨대setCookie("hashKey", rand(1000, 9000), 0)과 같이 설정하면 브라우저 창이 닫힐 때 까지만 키값이 유효하고 이후 사라집니다.
2. 연산 및 송수신 방식
- 클라이언트 페이지에서 쿠키나 세션 값을 검색하여 키값을 뽑아낸 뒤, 사용자 입력 비밀번호와 조합하여 메모리 상에서만 연산을 수행합니다.
- 예시로 클라이언트에서 비밀번호
1234에 키값0012를 더해1246이라는 해시된(변환된) 값을 만들고 이를 서버로 송신합니다. 서버는 수신한1246에서 해당 세션/쿠키의 키값을 역으로 빼거나 연산하여 원래의1234로 해석하여 인증 및 저장을 처리합니다. - 이 방식을 적용하면 네트워크 감시를 당하더라도 매번 바뀌는 유니크한 키값 때문에 해시된 스트링(
1246)이 수시로 변하므로, 탈취된 해시 문자열을 그대로 로그인 페이지에 재전송해도 유효하지 않게 되어 보안성을 높일 수 있습니다.
MySQL 및 PHP 비밀번호 해시 함수의 취약점 분석
MySQL의 password(), PHP의 md5(), crypt()와 같은 단방향 해시 함수는 디코딩을 지원하지 않아 안전하다고 여겨지기 쉽습니다. 하지만 DB가 유출되거나 해시된 결과값이 노출될 경우 브루트포스(무차별 대입) 공격에 취약하다는 한계가 있습니다.
주요 취약점 분석
- 무차별 대입 공격에 의한 원문 유출: 단방향 해시 함수는 같은 입력값에 대해 항상 동일한 해시값을 반환하므로, 공격자가 짧은 자리수의 숫자나 문자열을 반복해서 대입(
md5($pw)또는crypt($i,...)등)하여 해시값과 일치하는 원문을 쉽게 찾아낼 수 있습니다. - 네트워크 감시 및 세션 노출: 클라이언트와 서버 간 통신 시 비밀번호나 해시된 문자열을 그대로 전송하거나 폼 값으로 노출할 경우, 네트워크 감시를 통해 공격자가 원문을 알지 못하더라도 해시값 자체를 이용해 로그인 페이지로 우회 접속할 수 있습니다.
보안 강화 및 대안 가이드
개발자는 네트워크 감시나 DB 노출 등 어떠한 상황에서도 공격자가 비밀번호 정보에 접근할 수 있다고 가정하고 다음과 같은 보안 조치를 고려해야 합니다.
- 다중 해시 적용: 단일 해시 함수에만 의존하지 않고, 여러 해시 함수를 2중 3중으로 조합하여 사용합니다.
- 서버 고유의 토큰 키 조합: 외부에 노출되지 않는 서버 고유의 토큰 키값(예: 파일로 접근하며 접근 권한이 설정된 키)을 비밀번호 원문과 조합하여 저장 및 인증에 활용합니다.
- 1회용 세션/쿠키 키값 적용: 클라이언트와 통신할 때 접속 시마다 달라지는 유니크한 키값을 부여합니다. 이때 페이지나 임시 파일에 키값이 남지 않도록 lifetime이
0인 1회용 쿠키나 세션에 담아 메모리 상에서만 연산되도록 처리합니다.
현대적 PHP 표준 함수(password_hash, password_verify)와의 차이
최신 PHP 환경에서 권장하는 password_hash() 및 password_verify() 함수는 기존 레거시 함수(md5, crypt 등)와 비교했을 때 다음과 같은 차이와 장점이 있습니다.
- 솔트(Salt)의 자동 생성:
password_hash()는 자동으로 강력하고 무작위인 솔트를 생성하여 매번 다른 해시값을 만들어내므로 동일한 비밀번호라도 결과가 달라집니다. - 강력한 알고리즘 지원: Bcrypt, Argon2id 등 최신 해싱 알고리즘을 지원하여 GPU나 ASIC을 이용한 무차별 대입 공격에 대한 저항성이 높습니다.
MySQL 계정 로그인 실패 횟수 및 계정 잠금 정책 설정
MySQL 데이터베이스 서버 레벨에서 연속된 로그인 실패에 대응하기 위해 FAILED_LOGIN_ATTEMPTS와 PASSWORD_LOCK_TIME 정책을 설정하여 무차별 대입(Brute Force) 공격을 방어할 수 있습니다.
설정 방법 및 조건
- 적용 버전: MySQL 8.0.19 이상 환경에서 계정별 실패 횟수 제한 설정이 지원됩니다.
- 실패 횟수 설정 명령: 특정 사용자 계정에 대해 연속 로그인 실패 허용 횟수를 지정하려면 아래와 같이 실행합니다.
mysql> alter user 'dazemonkey'@'localhost' failed_login_attempts 5; - 현재 설정 확인: 사용자별 계정 및
failed_login_attempts값은 아래 쿼리로 확인할 수 있습니다.mysql> select user, failed_login_attempts from mysql.user;
다음 행동 및 주의사항
- 데이터베이스 서버의 버전과 플러그인 설정 상태에 따라 잠금 관련 변수 지원 여부가 다를 수 있으므로 현재 운영 중인 MySQL 버전을 먼저 확인해야 합니다.
상황별 보안 강화 가이드
- 내 상황: MySQL/PHP 환경(그누보드 등)에서 단방향 해시 함수(
md5(),crypt(),password())를 사용 중이거나 비밀번호 보안 설정을 강화하려는 경우 - 우선할 판단: 단일 해시 함수는 DB 노출 시 무차별 대입(Brute Force) 공격에 취약하므로 복합적인 보안 계층 적용 필요성 검토
- 다음 행동: 여러 해시 함수를 중첩하는 다중 해시 적용, 서버 고유 토큰 키(페퍼) 활용, 그리고 lifetime이 0인 1회용 세션/쿠키 키값을 통한 클라이언트 통신 보호 대책 수립
실전 FAQ
Q: MD5나 password()로 암호화된 비밀번호는 DB가 유출되면 정말 쉽게 뚫리나요?
- 실행 중 막히는 점: 해시 함수는 복호화가 불가능하다고 알려져 있어 안전할 것으로 오해하기 쉽습니다.
- 예외 및 대안: 동일한 입력값에 대해 항상 같은 해시값을 반환하므로, DB가 유출될 경우 공격자가 짧은 자리수의 숫자나 문자열을 반복 대입하여 원문을 쉽게 찾아낼 수 있습니다. 따라서 단일 해시 의존을 피하고 다중 해시 및 서버 토큰 키 조합을 적용해야 합니다.
함께 보면 좋은 글
- 오토셋 환경에서의 그누보드5 초기 설치 및 DB 설정 가이드
- MD5에서 SHA256으로 비밀번호 암호화 마이그레이션 및 하위 호환성 처리 방법
- 그누보드 비밀글 열람 시 매번 비밀번호 입력 강제 설정
참고자료
기타 참고자료
- 이전글아이코드 토큰키 기반 그누보드 SMS/LMS 자동 발송 모듈 연동 가이드 26.10.08
- 다음글PHP 및 그누보드에서 MySQL 마지막 PK(autoincrement) 값 획득 방법 26.10.07
댓글목록
등록된 댓글이 없습니다.