설명
PAM(Pluggable Authentication Modules)의 pam_authenticate()는 자격 증명을 검증하지만 계정 관리 정책 확인을 대신하지 않습니다. 인증 후 pam_acct_mgmt()의 결과를 확인하지 않으면 계정 만료나 접근 제한 같은 정책을 놓칠 수 있습니다.
잠재적 영향
- 계정 관리 정책에서 금지한 사용자가 로그인할 수 있습니다.
- 계정 또는 비밀번호의 만료 조건을 적절히 처리하지 못할 수 있습니다.
- 실제 영향은 서비스에 구성된 PAM 모듈과 계정 정책에 따라 달라집니다.
해결 방법
- 인증 성공 후
pam_acct_mgmt()로 계정 상태와 접근 조건을 확인하세요. 성공하지 않으면 그대로 접근을 허용하지 마세요. PAM_NEW_AUTHTOK_REQD는 비밀번호 교체가 필요한 상태입니다. 허용된 교체 절차를 완료하기 전에는 접근을 거부하세요.- 서비스에 필요한 인증·계정 관리 모듈과 최소 권한 정책을 구성하고, 비밀번호를 남기지 않도록 로그인 성공·실패를 기록하세요.
예시
PAM 바인딩의 흐름을 보여 주는 일부 코드입니다. pam_start, handle_conv의 바인딩, 비밀번호 전달과 pam_end 정리는 생략했습니다. 실제 구현에서는 초기화 성공 여부를 확인하고, 성공적으로 시작한 트랜잭션은 모든 종료 경로에서 정리하세요.
변경 전
python
from pam import PamHandle, PamConv
from ctypes import CDLL, c_int, byref
from ctypes.util import find_library
libpam = CDLL(find_library("pam"))
pam_authenticate = libpam.pam_authenticate
pam_authenticate.argtypes = [PamHandle, c_int]
pam_authenticate.restype = c_int
def authenticate(username, password, service='login'):
handle = PamHandle()
conv = PamConv(handle_conv, 0)
retval = pam_start(service, username, byref(conv), byref(handle))
if retval != 0:
return False
# 인증만 수행 (보안 취약)
return pam_authenticate(handle, 0) == 0
변경 후
python
from pam import PamHandle, PamConv
from ctypes import CDLL, c_int, byref
from ctypes.util import find_library
libpam = CDLL(find_library("pam"))
pam_authenticate = libpam.pam_authenticate
pam_authenticate.argtypes = [PamHandle, c_int]
pam_authenticate.restype = c_int
pam_acct_mgmt = libpam.pam_acct_mgmt
pam_acct_mgmt.argtypes = [PamHandle, c_int]
pam_acct_mgmt.restype = c_int
def authenticate(username, password, service='login'):
handle = PamHandle()
conv = PamConv(handle_conv, 0)
retval = pam_start(service, username, byref(conv), byref(handle))
if retval != 0:
return False
# 계정 상태 검증 추가 (안전)
return pam_authenticate(handle, 0) == 0 and pam_acct_mgmt(handle, 0) == 0
설명:
- 변경 전: 인증 결과만 확인하고 계정 관리 정책의 결과는 확인하지 않습니다.
- 변경 후: 인증과 계정 상태 검사가 모두 성공한 경우에만
True를 반환합니다.