Host 헤더 오염(Host Header Poisoning)으로 인한 이메일 링크 변조
Host Header Poisoning
설명
애플리케이션이 이메일 본문에 절대 URL을 만들 때 클라이언트가 보낸 Host 헤더(예: req.host, req.hostname, req.get('host'), headers().get('host'))를 그대로 사용하면 발생합니다. 공격자는 요청의 Host 헤더를 임의의 도메인으로 바꿔 전송하여, 비밀번호 재설정·이메일 인증 링크가 공격자 도메인으로 생성되게 만들 수 있습니다. 이렇게 생성된 링크를 사용자가 클릭하면 토큰(패스워드 리셋 토큰, 로그인 마법 링크 등)이 공격자에게 전송되어 계정 탈취나 피싱으로 악용될 수 있습니다.
잠재적 영향
계정 탈취: 비밀번호 재설정 토큰이나 이메일 인증 토큰이 공격자 도메인으로 전송되어 사용자의 계정을 장악할 수 있습니다.
민감 토큰 유출: 일회용 토큰, 세션 연계 값 등이 공격자에게 노출되어 추가 공격(세션 하이재킹 등)으로 이어질 수 있습니다.
피싱/브랜드 사칭: 합법적인 발신자의 이메일 안에 공격자 도메인이 포함되어 사용자를 속이기 쉬워집니다.
서비스 신뢰도 저하: 잘못된 링크 유포로 고객 신뢰가 하락하고 CS/보안 사고 비용이 증가합니다.
해결 방법
신뢰 가능한 베이스 URL을 구성에서만 사용: 환경 변수나 설정 파일에서 APP_BASE_URL(예: https://example.com)을 읽어 절대 URL을 생성하세요. 요청의 Host 값은 사용하지 마세요.
Host 기반 값 금지: 이메일 링크 생성 시 req.host, req.hostname, req.headers.host, req.get('host'), headers().get('host')를 사용하지 마세요.
허용 목록(allowlist) 검증: 멀티 테넌트 등으로 도메인이 여러 개인 경우, 테넌트별로 사전에 등록된 도메인 목록과 일치할 때만 사용하세요. 저장된 구성에서만 가져오고, 요청에서 파생하지 마세요.
취약한 코드 및 안전한 코드 예시
취약한 코드
안전한 코드
설명:
취약한 코드: req.get('host')와 같은 Host 헤더 기반 값을 사용하여 절대 URL을 만들면, 공격자가 Host 헤더를 임의 도메인으로 바꿔 요청을 보냈을 때 이메일 링크가 공격자 도메인으로 생성됩니다. 사용자가 그 링크를 클릭하면 비밀번호 재설정 토큰 등 민감 정보가 공격자에게 전송되어 계정 탈취로 이어질 수 있습니다.
안전한 코드: 앱이 신뢰 가능한 구성(APP_BASE_URL)에서만 베이스 URL을 읽고, 허용 목록으로 검증한 뒤 URL 객체로 절대 경로를 생성합니다. 요청의 Host 헤더를 전혀 사용하지 않으므로 Host 헤더 오염 공격으로 이메일 링크가 변조될 가능성을 차단합니다.
참조
Last updated