Jakarta Faces 출력 이스케이프 비활성화로 인한 XSS

Jakarta Faces 출력 이스케이프 비활성화

설명

Jakarta Faces의 표준 h:outputText, h:outputFormat, h:outputLabel 구성 요소는 기본적으로 HTML/XML 마크업에 민감한 문자를 이스케이프합니다. escape를 거짓으로 설정하면 이 보호가 비활성화되어 구성 요소 값이 마크업으로 렌더링됩니다. 값에 사용자 입력이나 다른 신뢰할 수 없는 데이터가 포함될 수 있으면 공격자가 실행 가능한 HTML 또는 스크립트를 삽입할 수 있습니다.

escape="false"만으로 공격이 성립하는 것은 아닙니다. 실제 XSS에는 신뢰할 수 없는 값이 해당 출력 지점까지 도달해야 합니다. 다만 이 설정은 Faces의 기본 출력 인코딩을 명시적으로 우회하므로 데이터 출처와 변환 과정을 함께 검토해야 합니다. Jakarta Faces 4.1의 VDL 문서는 세 구성 요소의 escape 기본값을 true로 정의합니다.

잠재적 영향

  • 피해자 권한으로 임의 동작 수행 또는 계정 가장
  • 페이지에 표시되는 민감정보 열람 및 외부 전송
  • 피싱 UI 삽입, 콘텐츠 변조, 악성 사이트로의 이동 유도

해결 방법

  • 일반 텍스트는 escape 속성을 생략하거나 escape="true"로 설정해 Faces의 기본 이스케이프를 유지합니다.
  • 입력값 검증이나 문자열 필터링을 출력 인코딩의 대체 수단으로 사용하지 않습니다. 값을 실제 출력 컨텍스트에 맞게 인코딩해야 합니다.
  • 사용자 작성 리치 HTML이 반드시 필요하면 원본을 그대로 출력하지 않습니다. 유지 관리되는 OWASP Java HTML Sanitizer의 최소 허용 목록 정책으로 서버 측에서 정화한 값만 렌더링하고, escape="false" 사용 지점의 정책과 데이터 흐름을 함께 검토합니다. sanitize 같은 함수 이름만으로 안전하다고 판단해서는 안 됩니다.
  • 콘텐츠 보안 정책(CSP)은 심층 방어 수단으로 사용하되 출력 인코딩이나 HTML 정화를 대신하지 않도록 합니다.

예시

변경 전

html
<ui:composition xmlns:ui="jakarta.faces.facelets"
                xmlns:h="jakarta.faces.html">
  <h:outputText value="#{profile.biography}" escape="false" />
</ui:composition>

변경 후

html
<ui:composition xmlns:ui="jakarta.faces.facelets"
                xmlns:h="jakarta.faces.html">
  <h:outputText value="#{profile.biography}" />
</ui:composition>

설명:

  • 변경 전: 사용자가 수정할 수 있는 소개문을 escape="false"로 렌더링하면 값에 포함된 마크업이 브라우저에서 해석됩니다.
  • 변경 후: escape를 생략해 기본값 true를 사용하므로 동일한 값이 HTML 텍스트로 인코딩됩니다.

참조