[Spring, Thymeleaf, JS] How, What - Markdown 적용, XSS 공격 방지

하쮸·2025년 11월 8일

Error, Why, What, How

목록 보기
51/68

1. 마크다운.


1-1. 의존성 관리.

  • 사용할 라이브러리를 가져옴.

  • 사용하고 싶은 버전을 골라서 build.gradle에 복붙해주면 됨.

1-2. @Configuration 설정.

import org.commonmark.parser.Parser;
import org.commonmark.renderer.html.HtmlRenderer;

@Configuration
public class MarkdownConfig {
    @Bean
    public Parser parser() {
        return Parser.builder().build();
    }
    @Bean
    public HtmlRenderer htmlRenderer() {
        return HtmlRenderer.builder().build();
    }
}
  • 설정을 담당하는 클래스.

    • 스프링은 @Configuration 클래스를 확인 후 내부에 정의되어 있는 @Bean 메서드들을 실행하고 반환되는 객체들을 스프링 컨테이너(IoC Container)에 등록함.

      • 즉, 마크다운(Markdown)으로 변환할 때 필요한 핵심 컴포넌트들을 스프링 컨테이너빈(Bean)으로 등록함.
    • @Bean은 메서드가 반환하는 객체를 스프링 빈(Bean)으로 컨테이너에 등록하도록 지시.

      • 등록된 빈(Bean)은 다른 곳에서 의존성 주입(Dependency Injection, DI)을 통해 사용할 수 있음.
  • 이렇게 함으로서 객체를 실행시 한 번만 생성하여 싱글톤(Singleton)으로 사용할 수 있음.


1-3. @Component 등록.

@Component
@RequiredArgsConstructor
public class Markdown {
    private final Parser parser;
    private final HtmlRenderer htmlRenderer;

    public String parseMarkdown(String markdown) {
        Node documentNode = parser.parse(markdown);
        return htmlRenderer.render(documentNode);
    }
}
  • @Component를 통해 해당 클래스를 스프링 컨테이너(IoC Container)빈(Bean)으로 등록함.
    • @Configuration클래스에서 @Bean으로 등록해놓은 객체들을 private final필드에 생성자 방식으로 주입시킴.
  • 그리고 이를 활용하여 사용할 메서드를 정의해주면 됨.

1-4. 뷰 템플릿.

  • @Component를 통해 클래스를 스프링 컨테이너(IoC Container)빈(Bean)으로 등록해놨기 때문에 Thymeleaf 뷰 템플릿에서 사용할 수 있음.
<div class="card-text" th:utext="${@markdown.parseMarkdown(article.content)}"></div>
  • @markdown : 스프링 컨테이너(IoC 컨테이너)에 등록되어 있는 Markdown이라는 이름의 빈을 찾음.
    • 빈 내부에 정의 되어 있는 parseMarkdown()메서드의 인자로 마크다운으로 변환하고 싶은 내용을 넣어주면 됨.

1-5. 확인.

- 마크
  - 다운
<h1>마크다운</h1>



![](https://velog.velcdn.com/images/.../image.png)
![](https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google_2015_logo.svg/1200px-Google_2015_logo.svg.png)

  • 잘 동작 되는 것을 확인.

2. 문제점.

  • 뷰 파일 코드에서 th:text 가 아닌 th:utext를 사용.
  • 이로 인해 XSS(Cross-Site Scripting, 교차 사이트 스크립팅)공격에 대해 취약해짐.

2-1. 테스트.

  • XSS 공격은 주로 JS(Java Script)를 사용.

  • 게시글 내용에 악성 <script> 태그를 포함해서 작성해버리면 이후 해당 글을 클릭하는 다른 사용자에게 해당 코드가 실행되어버림.

  • 페이지 소스 코드를 보면 <script>태그가 그대로 있음.

3. XSS 방어.


3-1. Jsoup 라이브러리.

  • 여러 방법과 라이브러리가 있겠지만 Jsoup가 가장 유명하고 표준으로 사용된다고 함.
  • Jsoup은 Java로 작성된 HTML 파서(Parser) 라이브러리.
    • HTML 문서의 분석, 조작, 그리고 보안을 위한 정제(Sanitization) 기능을 제공함.
    • 웹 스크래핑과 XSS 방어에 널리 사용되는, HTML을 다루는데 있어 자바의 표준 도구로 널리 사용됨.

3-2. 의존성 관리.

  • 사용할 라이브러리를 가져옴.
  • 사용하고 싶은 버전을 골라서 build.gradle에 복붙해주면 됨.

3-3. Jsoup 살펴보기.


3-3-1. Class Jsoup

  • Class Jsoup
    • 다양한 메서드에 대한 설명을 확인할 수 있음.

  • 위는 사용할 메서드이다.

  • 즉, clean()메서드의 매개변수를 확인해보면 (마크다운으로 변환할 HTML, 필터링(정제)할 요소)를 넘겨주면 됨.

3-3-2. Class Safelist

  • none(), simpleText(), basic(), basicWithImages(), relaxed()중 한 가지를 선택하는게 가장 기본.
    • 특히 basicWithImages()의 경우 기존에 있는 basic()에 이미지 관련된 것만 추가된 거임.
basic().addTags("img").addAttributes("img", "align", "alt", "height", "src", "title", "width").addProtocols("img", "src", "http", "https");

4. 테스트.

  • 이전 테스트와 마찬가지로 내용에 악성 <script> 태그를 포함해서 작성했지만 스크립트가 동작하지 않음.
    • 즉, Jsoup.clean()으로 인해 필터링(정제) 되었음.

  • 페이지 소스 보기를 해봐도 <script>태그는 안 보이는 것을 확인.
- ㅇㅇ
  - ㅇㅇ

<script>alert('XSS 공격');</script>

'``
<script>alert('XSS 공격');</script>
'``

`<script>`

<img src="https://upload.wikimedia.org/wikipedia/commons/thumb/2/2f/Google_2015_logo.svg/1200px-Google_2015_logo.svg.png" width="200" >

  • '''과 같은 코드블록이나 ' 백틱을 사용한 <script>는 정상적으로 보이는 것을 확인.

5. 참고.

profile
Every cloud has a silver lining.

0개의 댓글