크로스 사이트 스크립팅(XSS)이란 무엇이며, 어떻게 방어하나요?

김상욱·2024년 12월 29일
0

크로스 사이트 스크립팅(XSS)이란 무엇이며, 어떻게 방어하나요?

크로스 사이트 스크립팅(XSS)는 웹 애플리케이션의 보안 취약점 중 하나로, 공격자가 악성 스크립트를 웹 페이지에 삽입하여 다른 사용자에게 전달하는 공격 기법입니다 이를 통해 공격자는 사용자의 세션 쿠키 탈취, 파싱, 악성 코드 실행 등 다양한 악의적인 행위를 할 수 있습니다.

  1. 저장형 XSS (Stored XSS) : 공격자가 입력한 악성 스크립트가 서버에 저장되고, 다른 사용자가 해당 데이터를 조회할 때 스크립트가 실행됩니다. ex) 게시판에 악성 스크립트를 포함한 글을 작성

  2. 반사형 XSS (Reflected XSS) : 공격자가 조작한 URL이나 요청을 통해 악성 스크립트가 즉시 반영되어 실행됩니다. ex) 검색창에 스크립트를 삽입한 URL을 클릭했을 때 스크립트 실행.

  3. DOM 기반 XSS : 클라이언트 사이드에서 자바스크립트가 DOM을 조작하면서 발생하는 XSS 입니다. ex) urL의 파라미터를 자바스크립트로 처리할 때 검증 없이 삽입.

XSS공격을 방어하기 위해서는 입력 검증(Input Validation)과 출력 인코딩(Output Encoding)이 핵심입니다.

  1. 입력 검증(Input Validation) : 사용자가 입력한 데이터를 서버에서 신뢰할 수 없으므로, 입력 값을 검증하고 필터링해야 합니다.
  • Spring Security 사용 : 기본적인 XSS 방어 가능
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 다른 설정들...
            .headers()
                .xssProtection()
                .and()
                .contentSecurityPolicy("script-src 'self'");
    }
}
  • 유효성 검사 라이브러리 사용 : javax.validation 또는 Hibernate Validator 등을 사용해 입력 값을 검증.
import javax.validation.constraints.NotBlank;

public class UserInput {
    @NotBlank
    private String name;
    
    // getters and setters
}
  1. 출력 인코딩(Output Encoding) : 데이터를 사용자에게 출력할 때 HTML, JavaScript, URL 등의 컨텍스트에 맞게 인코딩하여 스크립트가 실행되지 않도록 합니다.
  • Thymeleaf 사용 시 : Thymeleaf는 기본적으로 HTML 인코딩을 제공합니다 ${variable} 대신 th:text를 사용하면 자동으로 인코딩되비다.
<p th:text="${userInput}">User Input</p>
  • Spring의 HtmlUtils 사용 : 서버 사이드에서 직접 인코딩이 필요할 때 사용할 수 있습니다.
import org.springframework.web.util.HtmlUtils;

String safeHtml = HtmlUtils.htmlEscape(userInput);
model.addAttribute("safeHtml", safeHtml);
  1. Content Security Policy(CSP) 설정 : CSP는 브라우저에게 어떤 리소스를 로드할 수 있는지 지시하는 보안 정책입니다. 이를 통해 악성 스크립트의 실행을 제한할 수 있습니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // 다른 설정들...
            .headers()
                .contentSecurityPolicy("default-src 'self'; script-src 'self'");
    }
}
  1. HTTPOnly 및 Secure 쿠키 설정 : 세션 쿠키에 HttpOnly 속성을 설정하면 자바스크립트를 통해 쿠키에 접근할 수 없어 세션 탈취를 방지할 수 있습니다.
@Bean
public HttpSessionIdResolver httpSessionIdResolver() {
    return HeaderHttpSessionIdResolver.xAuthToken();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        // 다른 설정들...
        .sessionManagement()
            .sessionFixation().migrateSession()
            .and()
        .csrf().disable()
        .headers()
            .httpStrictTransportSecurity()
            .and()
            .frameOptions().sameOrigin()
            .and()
            .sessionManagement()
                .maximumSessions(1)
                .expiredUrl("/login?expired");
}
  1. 라이브러리 및 프레임워크 최신화 : 보안 취약점은 지속적으로 발견되므로, 사용하는 라이브러리와 프레임워크를 최신 버전으로 유지하는 것이 중요합니다.

물론입니다! 신입 Java/Spring 백엔드 개발자로서 크로스 사이트 스크립팅(XSS)에 대해 이론을 이해하는 것뿐만 아니라 직접 실습을 통해 경험을 쌓는 것이 매우 중요합니다. 아래에 실습할 만한 단계별 프로젝트와 연습 과제를 제안드립니다.

실습 목표

  1. XSS 취약점 이해 및 탐지
  2. XSS 방어 기법 적용
  3. 보안 설정 확인 및 테스트

실습 환경 준비

  • Java JDK 11 이상
  • Spring Boot (버전 2.5 이상 추천)
  • IDE (IntelliJ IDEA, Eclipse 등)
  • Maven 또는 Gradle (프로젝트 관리 도구)
  • 웹 브라우저 (Chrome, Firefox 등)
  • Postman 또는 cURL (API 테스트 도구)

실습 1: 기본 Spring Boot 웹 애플리케이션 생성

  1. Spring Initializr를 사용하여 새로운 Spring Boot 프로젝트를 생성합니다.

    • Dependencies: Spring Web, Thymeleaf, Spring Security (선택 사항)
  2. 프로젝트 구조 설정

    • src/main/java/com/example/xssdemo
    • src/main/resources/templates
  3. 간단한 컨트롤러 작성

    package com.example.xssdemo.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    
    @Controller
    public class HomeController {
    
        @GetMapping("/")
        public String home() {
            return "home";
        }
    
        @PostMapping("/submit")
        public String submit(@RequestParam("input") String input, Model model) {
            model.addAttribute("userInput", input);
            return "result";
        }
    }
  4. Thymeleaf 템플릿 작성

    • src/main/resources/templates/home.html

      <!DOCTYPE html>
      <html xmlns:th="http://www.thymeleaf.org">
      <head>
          <title>XSS Demo</title>
      </head>
      <body>
          <h1>입력 폼</h1>
          <form action="/submit" method="post">
              <input type="text" name="input" />
              <button type="submit">제출</button>
          </form>
      </body>
      </html>
    • src/main/resources/templates/result.html

      <!DOCTYPE html>
      <html xmlns:th="http://www.thymeleaf.org">
      <head>
          <title>결과</title>
      </head>
      <body>
          <h1>입력 결과</h1>
          <p th:text="${userInput}">사용자 입력</p>
      </body>
      </html>
  5. 애플리케이션 실행 및 테스트

    • 애플리케이션을 실행한 후, 브라우저에서 http://localhost:8080으로 접속합니다.
    • 입력 폼에 간단한 텍스트를 입력하고 제출합니다.
    • result.html에서 입력한 내용이 안전하게 출력되는지 확인합니다.

실습 2: XSS 취약점 탐지

  1. 취약점 확인

    현재 result.html에서 사용자가 입력한 내용을 th:text를 사용하여 출력하고 있으므로, Thymeleaf는 기본적으로 HTML 이스케이프를 수행하여 XSS를 방어합니다. 이를 확인하기 위해 스크립트를 입력해봅니다.

    • 예시 입력: <script>alert('XSS');</script>
  2. 테스트 결과 확인

    • 만약 th:text 대신 ${userInput}을 직접 사용하면 XSS 취약점이 발생할 수 있습니다.
    • 이를 통해 Thymeleaf의 출력 인코딩 기능의 중요성을 이해할 수 있습니다.

실습 3: XSS 방어 기법 적용

  1. 입력 검증 강화

    • 유효성 검사 어노테이션 추가

      import javax.validation.constraints.NotBlank;
      import javax.validation.constraints.Size;
      
      public class UserInput {
          @NotBlank(message = "입력은 필수입니다.")
          @Size(max = 100, message = "입력은 100자 이내여야 합니다.")
          private String input;
      
          // getters and setters
      }
    • 컨트롤러 수정

      import javax.validation.Valid;
      import org.springframework.validation.BindingResult;
      
      @PostMapping("/submit")
      public String submit(@Valid UserInput userInput, BindingResult result, Model model) {
          if (result.hasErrors()) {
              return "home";
          }
          model.addAttribute("userInput", userInput.getInput());
          return "result";
      }
  2. Content Security Policy(CSP) 설정

    • Spring Security 설정 추가

      import org.springframework.context.annotation.Configuration;
      import org.springframework.security.config.annotation.web.builders.HttpSecurity;
      import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
      import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
      
      @Configuration
      @EnableWebSecurity
      public class SecurityConfig extends WebSecurityConfigurerAdapter {
          @Override
          protected void configure(HttpSecurity http) throws Exception {
              http
                  .authorizeRequests()
                      .antMatchers("/", "/submit").permitAll()
                      .anyRequest().authenticated()
                      .and()
                  .csrf().disable()
                  .headers()
                      .contentSecurityPolicy("default-src 'self'; script-src 'self'");
          }
      }
  3. HttpOnly 및 Secure 쿠키 설정

    • 세션 쿠키 설정

      import org.springframework.context.annotation.Bean;
      import org.springframework.security.config.annotation.web.builders.HttpSecurity;
      import org.springframework.security.web.SecurityFilterChain;
      
      @Configuration
      public class SecurityConfig {
      
          @Bean
          public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
              http
                  .csrf().disable()
                  .headers()
                      .contentSecurityPolicy("default-src 'self'; script-src 'self'")
                      .and()
                      .httpStrictTransportSecurity()
                      .and()
                      .frameOptions().sameOrigin()
                      .and()
                  .sessionManagement()
                      .sessionFixation().migrateSession();
      
              // 세션 쿠키 설정
              http
                  .sessionManagement()
                      .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                      .and()
                  .rememberMe()
                      .rememberMeCookieName("remember-me")
                      .tokenValiditySeconds(86400)
                      .and()
                  .logout()
                      .logoutUrl("/logout")
                      .logoutSuccessUrl("/");
      
              return http.build();
          }
      }
  4. Thymeleaf 자동 인코딩 확인

    • th:text가 제대로 작동하는지 다시 한 번 테스트해봅니다.
    • <script> 태그가 이스케이프되어 브라우저에서 스크립트가 실행되지 않아야 합니다.

실습 4: 자동화 도구를 사용한 XSS 테스트

  1. OWASP ZAP 사용

    • 설치 및 실행

    • 프로젝트 스캔

      • OWASP ZAP을 실행하고, 브라우저 프록시 설정을 ZAP으로 변경합니다.
      • 애플리케이션을 사용하면서 ZAP이 트래픽을 스캔하도록 합니다.
      • ZAP이 XSS 취약점을 탐지하면 보고서를 통해 확인할 수 있습니다.
  2. Burp Suite 사용

실습 5: 추가 보안 강화

  1. Spring Security CSRF 보호 활성화

    • CSRF 보호는 XSS와 직접적인 관련은 없지만, 전반적인 웹 보안을 강화합니다.
    • SecurityConfig에서 csrf().enable() 설정을 확인합니다.
  2. 입력 필터링 라이브러리 사용

    import org.owasp.html.PolicyFactory;
    import org.owasp.html.Sanitizers;
    
    public String sanitizeInput(String input) {
        PolicyFactory policy = Sanitizers.FORMATTING.and(Sanitizers.LINKS);
        return policy.sanitize(input);
    }
  3. 로그 모니터링 및 알림 설정

    • 보안 관련 로그를 모니터링하고, 이상 징후가 발견되면 즉시 대응할 수 있도록 설정합니다.

요약 및 추가 팁

  • 실습을 통한 이해: 이론을 배우는 것뿐만 아니라 실제로 코드를 작성하고, 취약점을 테스트하며, 방어 기법을 적용해보는 것이 중요합니다.
  • 보안 모범 사례 학습: OWASP의 Top Ten을 공부하여 다양한 웹 보안 취약점과 방어 방법을 익히세요.
  • 코드 리뷰 및 피드백: 작성한 코드를 동료나 커뮤니티에 공유하고, 피드백을 받아 개선해보세요.
  • 지속적인 학습: 보안은 빠르게 변화하는 분야이므로, 최신 보안 동향과 패치를 지속적으로 학습하는 습관을 가지세요.

이러한 실습을 통해 XSS뿐만 아니라 다른 보안 취약점에 대한 이해와 방어 능력을 키울 수 있습니다. 취업 준비에 많은 도움이 되길 바라며, 추가 질문이 있으면 언제든지 문의해주세요! 화이팅하세요!

0개의 댓글