설명
로그 인젝션은 신뢰할 수 없는 데이터가 출력 중립화 없이 줄 단위 로그에 기록될 때 발생합니다. 캐리지 리턴(\r, CR) 또는 라인 피드(\n, LF)는 레코드 경계를 새로 만들거나 변경할 수 있습니다. 공격자는 이를 이용해 가짜 로그 항목을 삽입하고, 자동 파싱을 손상시키거나, 모니터링 및 포렌식 분석을 오도할 수 있습니다.
Python의 logging API는 msg에 모든 위치 인자(args)를 대입해 최종 메시지를 만듭니다. 따라서 문자열 연결뿐 아니라 logging.info("User: %s", user)처럼 형식화 인자를 분리한 호출도 user에 포함된 CR/LF를 자동으로 중립화하지 않습니다.
잠재적 영향
- 로그 위조: 가짜 로그 라인을 삽입하거나 심각도와 형식을 조작해 보이게 할 수 있습니다.
- 자동 분석 교란: 레코드 형식을 깨뜨려 수집기나 SIEM의 파싱 및 경고 규칙을 오작동시킬 수 있습니다.
- 포렌식 무결성 훼손: 사건 타임라인과 사용자 활동 추적을 어렵게 만들어 조사와 대응을 지연시킬 수 있습니다.
해결 방법
- 줄 단위 로그에서는 신뢰할 수 없는 모든 메시지와 형식화 인자의 CR 및 LF를 로거에 전달하기 전에 안전한 문자로 치환합니다.
- 레코드 구분 문자를 이스케이프하고 이벤트마다 정확히 하나의 레코드를 출력하는, 검증된 직렬화 도구나 포매터를 사용합니다. 키-값 필드를 사용한다는 사실만으로 안전한 구조화 로깅이 보장되지는 않습니다.
- Python의 기본 압축형
json.dumps(...)는 CR, LF 및 U+0000부터 U+001F까지의 제어 문자를 이스케이프합니다. 줄 단위 로그에서는indent를 사용한 보기 좋은 출력이나 공격자가 제어하는 직렬화 옵션을 사용하지 않습니다. - 허용 목록은 허용되는 값 전체가 유한하며 신뢰할 수 있는 CR/LF 없는 리터럴일 때만 안전 근거로 사용합니다. 형식 및 길이 검증만으로는 레코드 구분 문자가 중립화되지 않습니다.
- 로그를 HTML로 표시한다면 해당 뷰어의 출력 지점에서 HTML 인코딩을 별도로 적용합니다. HTML 이스케이프는 저장되는 로그의 CR/LF를 중립화하지 않습니다.
예시
변경 전
python
import logging
from flask import Flask, request
app = Flask(__name__)
@app.route('/search')
def search():
term = request.args.get('term', '')
# 취약: 형식화 인자를 분리해도 term의 CR/LF는 그대로 남습니다.
logging.info("Search term: %s", term)
return "ok"
변경 후
CR/LF 치환
python
import logging
from flask import Flask, request
app = Flask(__name__)
@app.route('/search')
def search_safe():
term = request.args.get('term', '')
safe_term = term.replace("\r", " ").replace("\n", " ")
logging.info("Search term: %s", safe_term)
return "ok"
압축형 JSON 직렬화
python
import json
import logging
from flask import request
def log_search():
term = request.args.get('term', '')
logging.info(json.dumps({"event": "search", "term": term}))
설명:
- 변경 전:
term의\r또는\n은 형식화 이후에도 남으므로 로그가 여러 레코드처럼 보일 수 있습니다. - CR/LF 치환: 두 레코드 구분 문자를 모두 안전한 한 줄 문자로 바꾼 뒤 기록합니다.
- 압축형 JSON: 기본
json.dumps(...)가 문자열 내부의 제어 문자를 이스케이프합니다.indent를 사용하면 직렬화 결과 자체가 여러 줄이 되므로 줄 단위 로그에는 사용하지 않습니다.
적용 시 주의사항
실제 로그의 레코드 경계는 핸들러와 포매터 구성에 따라 달라집니다. 최종 출력에서 CR/LF가 안전하게 처리되는지 확인하고, 로그 뷰어의 HTML 인코딩도 별도로 적용하세요.
참조
- CWE-117: Improper Output Neutralization for Logs
- OWASP Logging Cheat Sheet
- OWASP Top 10:2025 A09 - Security Logging and Alerting Failures
- OWASP Top 10:2021 A09 - Security Logging and Monitoring Failures
- OWASP ASVS 5.0 V16 - Security Logging and Error Handling
- Python 3.14
logging문서 - Python 3.14
json문서 - Flask 3.1 로깅 문서