점프 투 스프링부트 XSS 공격 대비 개선

박철현·2023년 8월 13일
0

점프투스프링부트

목록 보기
11/14
  • XSS 공격 : 악의적인 사용자가 공격하려는 사이트에 악성 스크립트를 삽입할 수 있는 보안 취약점

  • C&C(좀비 PC에 명령을 내리거나 악성 코드를 제어하는 서버)로 리다이렉트 하거나 사용자의 쿠키를 탈취하여 세션 하이재킹 공격을 할 수 있다.

  • 현재 구현한 마크다운 기능은 타임리프 th:utext 문법을 사용해, 저장된 데이터를 그대로 다 렌더링 하기에 Stored XSS에 노출되어 있다. 즉 서버에 악성 스크립트가 저장되고 사용자가 질문이나 답변을 조회할 때 공격에 노출될 수 있다.

  • 교재에서 활용한 commonmark 라이브러리를 활용하여 기존 방식대로 마크다운 문법을 HTML 태그 등으로 전환하고, 렌더링해서 보여줄 때 일부 태그만 허용하도록 구현하여 XSS 공격을 대비하고자 함

  • OWASP Java HTML Sanitizer를 같이 사용하여 sanitizer 기능을 구현

    • sanitizer : 유해한 부분들 제거해줌
  • 의존성 추가

    • commonmark에서는 일부 HTML 태그의 경우 extension 의존성을 주입해야 sanitizer에 적용 가능합니다.
    • 마크다운 문법을 하나씩 다 적용해보며 태그가 생기나 안생기나, 꾸밈은 되나 안되나를 다 파악했습니다.
    • 테이블, 취소선, task 목록의 경우 무조건 있어야 꾸밈이 생깁니다!

  implementation 'org.commonmark:commonmark-ext-gfm-tables:0.20.0' // 테이블 관련 추가 설정
    implementation 'org.commonmark:commonmark-ext-gfm-strikethrough:0.21.0' // 취소선 관련 추가 설정
    implementation 'com.atlassian.commonmark:commonmark-ext-task-list-items:0.15.0' // task 관련 추가 설정

    implementation 'com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20211018.2'
  • 관련 기존 코드 개선

    • 기존 코드 일부
    Parser parser = Parser.builder().build();
    HtmlRenderer renderer = HtmlRenderer.builder().build();
    return renderer.render(document);
    • 매번 랜더링 될 때마다 Parser 객체와 HtmlRender 객체를 계속 생성

    • 질문에 달린 답변 하나하나 보여줄 때마다 다 생성, 질문도 생성

    • Bean에 등록하고 의존성 주입 받으면 계속 생성할 필요가 없어 개선

    • 개선

    	// 마크다운 렌더링 렌더러 및 파서 빈 등록
    		@Bean
    		HtmlRenderer htmlRenderer(List<Extension> extensions) {
    			return HtmlRenderer.builder()
    				.extensions(extensions)
    				.build();
    		}
    
    		@Bean
    		Parser parser(List<Extension> extensions) {
    			return Parser.builder()
    				.extensions(extensions)
    				.build();
    		}
    
    		@Bean
    		List<Extension> markdownExtensions() {
    			return Arrays.asList(
    				TablesExtension.create(),
    				StrikethroughExtension.create(),
    				TaskListItemsExtension.create()
    			);
    		}
    • 이런식으로 빈으로 등록해뒀습니다.
    • Extension은 위 이미지 중 commonmark에서 확장 등록 관련해서, 의존성 주입하고 추가하면 되는 부분입니다.
    • 테이블, 취소선, task 관련 의존성을 주입받았습니다.
  • CommonUtil 클래스 개선

@Component
@RequiredArgsConstructor
public class CommonUtil {
	private final Parser parser;
	private final HtmlRenderer renderer;

	public String markdown(String markdown) {
		Node document = parser.parse(markdown);
		String html = renderer.render(document);

		System.out.println("Converted HTML: " + html);

		// Sanitize HTML
		PolicyFactory policy = new HtmlPolicyBuilder()
			.allowElements("h1", "h2", "h3", "p", "b", "i", "em", "strong", "img", "a", "ul", "ol", "li", "table", "thead", "tbody", "tr", "th", "td", "del", "blockquote", "code", "pre", "input", "hr")
			.allowUrlProtocols("https", "http")
			.allowAttributes("href", "target").onElements("a")
			.allowAttributes("src", "alt").onElements("img")
			.allowAttributes("type", "checked", "disabled").onElements("input")
			.allowAttributes("border", "cellspacing", "cellpadding").onElements("table")
			.requireRelNofollowOnLinks()
			.toFactory();

		String safeHtml = policy.sanitize(html);
		System.out.println("After sanitize: " + safeHtml);
		return safeHtml; // Return the sanitized HTML
	}
}
  • 허용할 태그들을 하나씩 다 지정했습니다.
  • 속성이 필요한 태그들은 속성도 명시를 해주었고, 명시가 안된 태그들은 속성 허용하지 않습니다.
  • 마크다운 문법 하나하나 해가며 코드보기 등으로 노가다를 뛰었습니다 하하 적용하실 분들은 복붙하시면 될 것 같습니다.
  • 아 그리고 HTML 태그가 생겨도 일부는 바로 꾸밈이 되지 않기에 CSS를 GPT에게 물어봐서 적용 시켰습니다.
  table {
    border-collapse: collapse;  /* 테두리 */>
}

thead {
    background-color: #91c0fd;
    font-weight: bold;
    color: white;
    padding: 12px;
    text-align: center;
}

th, td {
    padding: 12px;
    border: 3px solid #91c0fd;
}
 /* 인용 */
blockquote {
    /* 왼쪽 경계선 */
    border-left: 4px solid #cccccc;
    /* 들여 쓰기와 여백 */
    padding-left: 20px;
    margin-left: 0;
    /* 글꼴 속성 (기울어짐, 색상 등) */
    font-style: italic;
    color: #666666;
}

a {
    text-decoration: none;
    color: inherit;
}

img {
    max-width: 100%;
}
  • 찾던 과정은 이렇습니다.

  • TaskList를 예시로 들면

    • 우선 commonmark GitHub에 readme 문서에서 찾으면 이슈를 찾습니다.
    • 관련 이슈가 있나 검색해보고, 없음을 확인하고 이것저것 삽질하다가 우선 저 파일을 찾아봤습니다.
    • 파일을 보니 PR로 바로 갈 수 있어 PR로 접속해서 파일 체인지를 봐서 버전을 확인하고 Gradle에 적용시켰습니다.
  • 아 그리고 마크다운 Code 방식, Code 태그는 생기는데 꾸밈이 안생깁니다.

  • 이는 highlight.JS 라는 라이브러리를 사용하여 해결하였습니다.

  • 적용 시키기 위해서는 pre태그로 code 태그가 감싸줘야 하기에 문서 로드될 때 자바스크립트 함수로 감싸주도록 하였습니다.

<pre><code></code></pre>
  • 적용 후

  • 진짜 그냥 마크다운 쓰는 것 처럼 깔끔하게 나옵니다!

  • 뭔가 적용을 시킨 블로그가 없는 것 같아 노가다로 한 4시간이 걸린 것 같습니다 하하 혹시 이런 방식을 사용하실 예정이라면 제 코드 가져가시면 될 것 같습니다!

  • 전체 코드 : PR 바로가기

  • 참조

profile
비슷한 어려움을 겪는 누군가에게 도움이 되길

0개의 댓글