FlawOpen

flawopen.com/SSTI/Jinja2

Jinja2 render_template_string()은 안전한가요?

CWE-1336: 서버 측 템플릿 인젝션참고 페이지
짧은 답변

템플릿 문자열을 애플리케이션 코드가 고정한 경우에만 render_template_string()을 사용하세요. Jinja가 파싱하기 전에 신뢰할 수 없는 입력을 문자열에 삽입하면 서버 측 템플릿 인젝션(SSTI)이 발생합니다. Flask의 기본 비샌드박스 Jinja 환경에서는 접근 가능한 객체, 설정, 애플리케이션에 따라 코드 실행까지 이어질 수 있지만 모든 배포에서 자동으로 발생하지는 않습니다.

취약한 코드
# All four are the same bug
render_template_string(f"Hi {name}")
render_template_string("Hi " + name)
render_template_string("Hi %s" % name)
render_template_string("Hi {}".format(name))

# A very common real-world shape:
@app.errorhandler(404)
def not_found(e):
    return render_template_string(
        f"<h1>No such page: "
        f"{request.path}</h1>"
    ), 404
# The URL path is attacker-controlled.
수정됨
# Template is a literal; the user value
# arrives as a named context variable
render_template_string(
    "Hi {{ name }}", name=name
)

# Better still, a real template file
render_template("greeting.html",
                name=name)

# For the 404 case, no template
# evaluation is needed at all:
return render_template("404.html",
                       path=request.path), 404

공격자가 템플릿 소스를 제어할 때의 위험

Flask는 기본적으로 config, request, session 같은 애플리케이션 전역 객체를 템플릿에 제공합니다. 공격자가 템플릿 소스를 제어하면 Jinja는 해당 환경에서 접근 가능한 객체를 사용해 표현식을 평가합니다. 영향은 애플리케이션이 노출한 객체와 기능에 따라 달라집니다. 무해한 산술 표현식은 평가 여부를 확인할 수 있지만, 그 자체로 코드 실행이나 접근 가능한 데이터를 입증하지는 않습니다.

애플리케이션이 민감한 값을 컨텍스트에 넣었다면 템플릿에서 노출될 수 있습니다. 예를 들어 Flask의 config에는 SECRET_KEY나 데이터베이스 설정이 있을 수 있지만 배포마다 다릅니다. 템플릿에 보이는 데이터에 비밀값을 넣지 말고 출력 이스케이프를 템플릿 평가의 방어 경계로 여기지 마세요.

CVE-2024-56201은 별개의 사례입니다

이 문제는 일반적인 SSTI와 별개인 Jinja 컴파일러 취약점이며 일반 SSTI의 전제 조건은 아닙니다. CVE-2024-56201은 공격자가 템플릿 내용과 이름을 모두 제어할 수 있을 때 Jinja 3.0.0~3.1.4에 영향을 줍니다. 이 조건에서는 샌드박스를 사용하더라도 임의의 Python 코드 실행이 가능했습니다. Jinja 3.1.5에서 수정되었습니다. 앱이 공격자 제공 템플릿이나 이름을 받는다면 권고문을 확인하고 업그레이드하세요.

주로 나타나는 위치

요청 경로를 출력하는 오류 페이지, 이메일 템플릿 관리 기능, 테넌트별 사용자 지정 기능, 공개해서는 안 되는 미리보기나 디버그 엔드포인트에서 이런 패턴이 나타날 수 있습니다.

자주 묻는 질문

autoescape가 이를 막아 주나요?

아니요. autoescape는 렌더링된 값을 이스케이프해 출력의 XSS 위험을 낮춥니다. 사용자 문자열이 템플릿 소스로 컴파일되는 것은 막지 않습니다. 서로 다른 처리 단계의 방어입니다.

이메일 사용자 지정처럼 사용자가 템플릿을 제공해야 한다면요?

Jinja는 사용자 템플릿용 SandboxedEnvironment를 제공하지만 문서에서는 완벽한 보안이 아니라고 경고합니다. 필요한 데이터만 전달하고 부작용이 있는 객체를 피하며 템플릿 엔진 외부에서 CPU, 메모리, 프로세스에 제한을 두세요. 샌드박스가 파싱 전 신뢰할 수 없는 입력을 삽입하는 방식을 안전하게 만들지는 않습니다.

기존 코드에서 어떻게 찾나요?

render_template_string을 검색하고 첫 번째 인수가 앱이 관리하는 고정 문자열인지 확인하세요. 사용자 값은 이름 있는 컨텍스트 변수로 전달하거나 템플릿 파일을 사용하세요. 동적으로 템플릿을 컴파일하는 API와 템플릿 이름을 제어할 수 있는 경로도 살펴보세요.

참고 자료

함께 보기 SSTI: 템플릿 인젝션의 원인과 수정 방법 서버 사이드 템플릿 인젝션이란? FreeMarker·Velocity SSTI: 안전한 템플릿 사용