본문 바로가기
시큐어코딩/JAVA

[시큐어코딩]Java에서 XSS(크로스 사이트 스크립트) 취약점 완벽 대응 가이드

by ICT리더 리치 2025. 4. 6.

XSS(Cross-Site Scripting)는 웹에서 가장 흔하게 발생하는 보안 취약점 중 하나입니다. Spring Boot 환경에서도 View 처리 방식에 따라 발생 가능성이 있으며, 정확한 이해와 사전 대응이 매우 중요합니다.

이번 포스팅에서는 Java 11 기반 Spring Boot 애플리케이션에서 발생할 수 있는 XSS 취약점의 원리와 대응 전략을 취약한 코드 vs 안전한 코드 형식으로 명확하게 비교합니다. 실제 코드를 기반으로 Request Wrapper 및 XSS Filter 도입 방법까지 설명드리겠습니다.

1. XSS란 무엇인가?

XSS(Cross-Site Scripting)는 공격자가 웹페이지에 악의적인 스크립트를 삽입해, 다른 사용자의 브라우저에서 실행되도록 유도하는 대표적인 보안 취약점입니다. 보통 입력 폼, 게시판, 댓글, 검색창 등 사용자 데이터를 렌더링하는 위치에서 발생합니다.

Spring Boot와 같은 Java 기반 웹 프레임워크에서도 Thymeleaf, JSP, JSON 응답 등 다양한 출력 경로에서 XSS가 발생할 수 있으며, 적절한 Escape 처리나 필터링이 없을 경우 심각한 정보 탈취나 세션 하이재킹으로 이어질 수 있습니다.

2. 입력값 검증 없는 출력 - 취약한 코드 예제

사용자의 입력값을 escape 처리 없이 모델에 바로 추가하여 View에서 그대로 출력하는 경우 XSS 공격이 발생할 수 있습니다. 특히 관리자 페이지 또는 공용 게시판에서 매우 치명적입니다.

 

@Controller
public class CommentController {

  @PostMapping("/comment")
  public String postComment(@RequestParam String message, Model model) {
    
    // 사용자 입력값을 escape 없이 출력에 사용
    model.addAttribute("msg", message);
    return "result"; // result.html에서 ${msg} 그대로 출력
  }
}

이 코드는 입력값 검증이나 이스케이프 없이 템플릿에 노출되기 때문에 사용자가 ` <script_>alert('XSS')</script_> ` 와 같은 코드를 입력하면 해당 스크립트가 실행되어 다른 사용자에게 악성 행동을 유발할 수 있습니다.

3. XSS 필터 적용으로 안전한 코드 구현

Spring Boot의 Filter와 RequestWrapper를 활용하여 요청 파라미터를 전역에서 감지하고 HTML 특수문자를 이스케이프 처리할 수 있습니다. 이 구조는 View, JSON 등 출력방식과 무관하게 XSS를 일괄 방지할 수 있는 실무적 방식입니다.

 

// XSSFilter.java – 전역 필터 등록
@Component
public class XSSFilter implements Filter {
  @Override
  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
          throws IOException, ServletException {
    HttpServletRequest req = (HttpServletRequest) request;
    chain.doFilter(new XSSRequestWrapper(req), response);
  }
}

// XSSRequestWrapper.java – 파라미터 정제
public class XSSRequestWrapper extends HttpServletRequestWrapper {

  public XSSRequestWrapper(HttpServletRequest request) {
    super(request);
  }

  @Override
  public String getParameter(String name) {
    return cleanXSS(super.getParameter(name));
  }

  private String cleanXSS(String value) {
    if (value != null) {
      value = value.replaceAll("&", "&");
      value = value.replaceAll("<", "<");
      value = value.replaceAll(">", ">");
      value = value.replaceAll("\"", """);
      value = value.replaceAll("'", "'");
    }
    return value;
  }
}

// WebConfig.java – 필터 등록
@Configuration
public class WebConfig {
  @Bean
  public FilterRegistrationBean xssFilter() {
    FilterRegistrationBean registration = new FilterRegistrationBean<>();
    registration.setFilter(new XSSFilter());
    registration.addUrlPatterns("/*");
    return registration;
  }
}

이 구조는 필터 레벨에서 모든 입력값을 감지하여 HTML 태그를 이스케이프 처리하므로 Spring Boot의 Controller, View, REST 응답 등 출력 방식에 관계없이 XSS 공격을 근본적으로 차단할 수 있는 효과적인 방법입니다.

4. 실무 Q&A – Spring Boot 필터 적용

Q 필터 방식은 View뿐 아니라 REST API에도 적용되나요?

적용됩니다. XSSRequestWrappergetParameter()를 통해 요청값을 읽는 모든 컨트롤러와 API 응답에 영향을 미칩니다. 단, JSON Body는 별도 처리 또는 Jackson 모듈과 연동해야 합니다.

 

Q Thymeleaf를 사용하는 경우에도 XSS가 발생하나요?

기본적으로 th:text는 자동으로 HTML Escape 처리되지만, th:utext를 사용할 경우 이스케이프가 되지 않아 취약해질 수 있습니다. 외부 데이터가 utext로 출력되는 경우 반드시 필터를 병행해야 합니다.

 

Q HTML 이스케이프 외에도 CSP(Content-Security-Policy)를 설정해야 하나요?

필수는 아니지만 매우 권장됩니다. CSP는 브라우저에서 스크립트 실행을 제어하는 보안 헤더로 XSS를 차단하는 2차 방어선 역할을 합니다. 특히 외부 JS 불허, inline-script 제한 등이 효과적입니다.

 

Q 필터가 작동하지 않는 경우는 언제인가요?

스프링 시큐리티 등 다른 필터 체인보다 먼저 등록되지 않으면 실행 순서에서 밀릴 수 있습니다. FilterRegistrationBeansetOrder()를 통해 우선순위를 설정하세요.

5. 마무리 요약 및 체크리스트

XSS는 단순하지만 매우 강력한 보안 취약점으로, 사용자 입력을 출력하는 모든 포인트에서 발생할 수 있습니다. Spring Boot 환경에서도 필터 기반 구조를 통해 효과적으로 대응할 수 있습니다.

  • 입력값은 항상 Escape 또는 필터 처리
  • Thymeleaf 사용 시 utext 대신 text 사용 권장
  • RequestWrapper 기반 XSS 필터 적용
  • REST 응답은 ObjectMapper 설정 또는 Escape 처리 필수
  • 가능하면 CSP 설정으로 이중 방어 구성
반응형