- 출처 / 참고: Microsoft Learn (Troubleshoot Error 18456 - Login failed for user), Microsoft Documentation (SQL Server Error Log)
명령어:
sqlcmd,grep,tail,journalctl,systemctl
키워드: Error 18456, Login failed for user, State Code, SQL Server Authentication, Mixed Mode, sys.server_principals, ERRORLOG
사용처: 애플리케이션 및 DBA 클라이언트(SSMS, DBeaver)에서 MSSQL 접속 거부 시 정확한 실패 원인(비밀번호, 비활성화, 인증 모드 등) 규명 및 계정 복구
실행예제
클라이언트(SSMS, DBeaver, 애플리케이션)에서 접속을 시도할 때 보안상 세부 정보가 숨겨진 채 Login failed for user '...'. (Microsoft SQL Server, Error: 18456) 메시지만 반환되므로, SQL Server의 ERRORLOG 파일을 확인하여 실제 실패 원인을 나타내는 State 코드를 확인합니다.
# 1. 클라이언트 관점: sqlcmd 명령어로 접속 시도 및 오류 메시지 확인
$ sqlcmd -S 192.168.1.100 -U appuser -P "WrongPassword123!"
# Msg 18456, Level 14, State 1, Server SQLSRV01, Line 1
# Login failed for user 'appuser'.
# -> 클라이언트에는 보안상 항상 'State 1'로만 표시됨 (실제 State는 서버 에러로그에 기록됨)
# 2. 리눅스 환경: SQL Server ERRORLOG 파일 경로 확인 및 최근 로그인 실패 로그 추적
$ sudo tail -n 50 /var/opt/mssql/log/errorlog | grep -E "18456|Login failed"
# [출력 예시 - 암호 불일치 (State 8)]
# 2026-10-16 10:14:22.15 Logon Error: 18456, Severity: 14, State: 8.
# 2026-10-16 10:14:22.15 Logon Login failed for user 'appuser'. Reason: Password did not match that for the login provided. [CLIENT: 192.168.1.50]
# [출력 예시 - 계정 없음 (State 5)]
# 2026-10-16 10:15:01.32 Logon Error: 18456, Severity: 14, State: 5.
# 2026-10-16 10:15:01.32 Logon Login failed for user 'guest_user'. Reason: Could not find a login matching the name provided. [CLIENT: 192.168.1.50]
# [출력 예시 - 혼합 인증 모드(Mixed Mode) 미활성화 (State 58)]
# 2026-10-16 10:16:11.89 Logon Error: 18456, Severity: 14, State: 58.
# 2026-10-16 10:16:11.89 Logon Login failed for user 'sa'. Reason: An attempt to login using SQL authentication failed. Server is configured for Integrated authentication only. [CLIENT: 192.168.1.50]
# 3. 윈도우 환경: PowerShell을 통해 ERRORLOG 내 18456 오류 필터링
# Get-Content "C:\Program Files\Microsoft SQL Server\MSSQL16.MSSQLSERVER\MSSQL\Log\ERRORLOG" | Select-String "18456" -Context 0,1
# 4. 관리자 계정으로 sys.server_principals를 조회하여 계정 활성 및 잠금 상태 점검
$ sqlcmd -S localhost -U sa -P 'MyStrongPass!' -Q "
SELECT name, is_disabled, create_date, modify_date
FROM sys.server_principals
WHERE name = 'appuser';
"
스크립트
리눅스 상에서 동작하는 SQL Server(mssql-server)의 ERRORLOG를 분석하여 발생한 Error 18456의 State 코드를 자동으로 추출하고, Microsoft 공식 State 명세에 따른 원인 및 해결 조치 가이드를 즉시 매핑해 주는 진단 쉘 스크립트입니다.
#!/bin/bash
# diagnose_mssql_18456.sh - Parse SQL Server ERRORLOG and explain Error 18456 State Codes
ERRORLOG_PATH=${1:-"/var/opt/mssql/log/errorlog"}
echo "=========================================="
echo " [SQL Server Error 18456 State 진단기]"
echo " 대상 로그 경로: ${ERRORLOG_PATH}"
echo "=========================================="
if [ ! -f "$ERRORLOG_PATH" ]; then
echo " [오류] ERRORLOG 파일을 찾을 수 없습니다: ${ERRORLOG_PATH}"
echo " -> 사용법: sudo $0 [ERRORLOG 경로]"
exit 1
fi
echo -e "\n[1] 최근 감지된 Error 18456 로그인 실패 내역 (최근 5건):"
RECENT_ERRORS=$(grep -n "Error: 18456" "$ERRORLOG_PATH" | tail -n 5)
if [ -z "$RECENT_ERRORS" ]; then
echo " [정상] 로그 파일 내에 Error 18456 기록이 없습니다."
exit 0
fi
while read -r LINE; do
LINE_NO=$(echo "$LINE" | cut -d':' -f1)
LOG_TEXT=$(echo "$LINE" | cut -d':' -f2-)
# State 코드 추출
STATE_CODE=$(echo "$LOG_TEXT" | grep -oE "State: [0-9]+" | awk '{print $2}')
# 다음 줄(이유 및 계정/클라이언트 IP 정보) 추출
NEXT_LINE=$(sed -n "$((LINE_NO + 1))p" "$ERRORLOG_PATH")
USER_NAME=$(echo "$NEXT_LINE" | grep -oE "user '[^']+'" | cut -d"'" -f2)
CLIENT_IP=$(echo "$NEXT_LINE" | grep -oE "\[CLIENT: [^]]+\]")
echo "------------------------------------------------------------------"
echo " 발생 시각 및 로그 : $LOG_TEXT"
echo " 대상 계정 / 클라이언트 : ${USER_NAME:-"알 수 없음"} / ${CLIENT_IP:-"[Local]"}"
echo " 검출된 State 코드 : State ${STATE_CODE}"
echo " [원인 및 해결 가이드]:"
case "$STATE_CODE" in
2)
echo " -> [원인] 유효하지 않은 사용자 ID (User ID is not valid)."
echo " -> [조치] 로그인 계정 철자 오타를 확인하거나 계정을 새로 생성하십시오."
;;
5)
echo " -> [원인] 존재하지 않는 로그인 계정 (User ID doesn't exist)."
echo " -> [조치] sys.server_principals 목록에서 계정 존재 여부를 확인하십시오."
;;
6)
echo " -> [원인] Windows 도메인 로그인 이름 해석 실패."
echo " -> [조치] Active Directory 도메인 컨트롤러 연결 상태 및 계정명을 확인하십시오."
;;
7)
echo " -> [원인] 계정이 비활성화(Disabled) 상태입니다."
echo " -> [조치] ALTER LOGIN [계정명] ENABLE; 명령으로 활성화하십시오."
;;
8)
echo " -> [원인] 비밀번호 불일치 (Password did not match)."
echo " -> [조치] 입력한 패스워드를 재확인하거나 ALTER LOGIN으로 재설정하십시오."
;;
11|12)
echo " -> [원인] 기본 데이터베이스(Default Database) 접근 권한 없음 또는 오프라인 상태."
echo " -> [조치] 계정의 Default DB 상태(ONLINE)를 확인하거나 ALTER LOGIN [계정명] WITH DEFAULT_DATABASE = [master]; 로 변경하십시오."
;;
16)
echo " -> [원인] 로그인 계정에 DB 엔진 접속 권한이 거부됨 (CONNECT SQL Denied)."
echo " -> [조치] GRANT CONNECT SQL TO [계정명]; 명령을 실행하십시오."
;;
38)
echo " -> [원인] 데이터베이스가 단일 사용자 모드(SINGLE_USER)로 설정되어 있어 이미 다른 세션이 선점함."
echo " -> [조치] DB를 MULTI_USER 모드로 변경하거나 선점 중인 세션을 정리하십시오."
;;
58)
echo " -> [원인] 서버가 Windows 인증 전용으로 설정되어 SQL Server 인증을 거부함."
echo " -> [조치] 서버 인증 모드를 'SQL Server 및 Windows 인증 모드(Mixed Mode)'로 변경 후 서비스를 재기동하십시오."
;;
*)
echo " -> [안내] 세부 원인 로그를 참조하십시오: $NEXT_LINE"
;;
esac
done <<< "$RECENT_ERRORS"
echo -e "\n=========================================="
echo " [진단 완료]"
echo "=========================================="
해설
Error 18456: Login failed for user는 클라이언트가 데이터베이스 엔진에 로그인 인증을 요청했으나 거부되었을 때 반환되는 가장 포괄적인 인증 실패 오류입니다.
1. 왜 클라이언트에는 항상 State 1로만 표시되는가
- 악의적인 공격자가 무차별 대입 공격(Brute-Force)을 수행할 때 “아이디가 틀렸는지”, “비밀번호가 틀렸는지”, “계정이 잠겼는지”를 알 수 없도록 방어하기 위한 보안 규격(Information Disclosure 방지) 때문입니다.
- 따라서 클라이언트에 찍히는
State: 1은 “구체적인 이유는 서버 관리자만 확인할 수 있음”을 뜻하며, 진짜 실패 원인은 반드시 데이터베이스 서버의ERRORLOG에 기록된 내부 State 코드를 통해서만 파악할 수 있습니다.
2. 핵심 State 코드별 상세 분석표
| State 코드 | 서버 ERRORLOG에 기록되는 사유 | 발생 원인 및 복구 쿼리 |
|---|---|---|
| State 2, 5 | Could not find a login matching the name provided. |
계정명이 없거나 오타 발생. 계정 생성 필요. |
| State 7 | Login is disabled. |
계정이 비활성화됨.ALTER LOGIN [appuser] ENABLE; |
| State 8 | Password did not match that for the login provided. |
비밀번호 불일치.ALTER LOGIN [appuser] WITH PASSWORD = 'NewPassword!'; |
| State 11, 12 | Cannot open user default database. |
로그인 시 기본 지정된 DB가 삭제/오프라인이거나 권한 없음.ALTER LOGIN [appuser] WITH DEFAULT_DATABASE = master; |
| State 16 | Login has not been granted CONNECT SQL permission. |
엔진 접속 권한 거절.GRANT CONNECT SQL TO [appuser]; |
| State 58 | Server is configured for Integrated authentication only. |
혼합 인증 모드 미설정. SQL Server 인증을 거부함. |
3. 주요 복구 가이드
1) 혼합 인증 모드(Mixed Mode) 전환 (State 58 조치)
SQL Server 설치 초기 기본값이 “Windows 인증 전용”으로 되어 있는 경우 sa나 일반 SQL 계정 인증이 일절 불가능합니다.
- Linux 환경 (
mssql-conf):sudo /opt/mssql/bin/mssql-conf set network.enable-mixed-mode true sudo systemctl restart mssql-server - Windows 환경 (T-SQL):
-- 인증 모드를 Mixed Mode(2)로 변경 EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2; -- 적용 후 SQL Server 서비스 재시작 필요
2) 계정 잠금 해제 및 비밀번호 정책 완화 (State 7, 8 조치)
운영체제의 암호 정책(복잡도, 만료, 잠금)으로 인해 계정이 잠긴 경우 잠금을 해제합니다.
ALTER LOGIN [appuser] WITH
PASSWORD = 'SafeStrongPassword2026!' UNLOCK,
CHECK_POLICY = OFF, -- 정책 강제 검사 비활성화 (필요 시)
CHECK_EXPIRATION = OFF; -- 만료 주기 비활성화
주의사항
- 클라이언트 화면만 보고 트러블슈팅 금지:
- 웹 애플리케이션의 콘솔이나 SSMS 팝업에 나타나는
State 1문자열은 유효한 단서가 아닙니다. 반드시 DB 서버의ERRORLOG위치를 확인하고 실제 서버 측 State 번호를 수집해야 엉뚱한 설정 변경으로 인한 2차 장애를 막을 수 있습니다.
- 웹 애플리케이션의 콘솔이나 SSMS 팝업에 나타나는
- 반복적인 비밀번호 실패에 따른 계정 잠금(Lockout):
CHECK_POLICY = ON으로 설정된 계정은 Windows 또는 Linux PAM의 보안 정책에 따라 비밀번호를 일정 횟수 이상 틀릴 경우 계정이 잠겨 State 8 대신 State 7이 발생할 수 있습니다. 애플리케이션의 커넥션 풀 설정 파일(예: 암호화된 패스워드 설정 등)이 최신화되어 있는지 확인해야 합니다.
- 기본 데이터베이스(Default DB) 변경 시 주의:
- 계정의 기본 DB(
DEFAULT_DATABASE)로 특정 업무 DB를 지정해 둔 상태에서 해당 DB를 백업 복원을 위해 오프라인으로 전환하거나 삭제하면, 계정 암호가 맞아도 State 11/12 에러를 내며 접속이 완전히 거부됩니다. 항상 기본 DB 접근 실패 시에는master데이터베이스로 접속 경로를 우회 지정하여 진단해야 합니다.
- 계정의 기본 DB(