<script>
태그만 XSS 공격에 사용될 줄 알았지만, 찾아보니 다른 사례도 많이 알려져있다.<script>
태그 인젝션<script>alert('XSS!');</script>
<img>, <svg>, <body>
등 거의 모든 태그에 onerror, onclick, onload 같은 속성을 달아 스크립트를 실행<img src="x" onerror="alert('XSS via onerror')">
<a href="#" onclick="stealCookies()">Click me</a>
javascript: URL
스킴<a href="javascript:alert('XSS via JS URI')">Click</a>
<iframe src="data:text/html;base64,PHNjcmlwdD5hbGVydCgnV2l0aCBkYXRhJyk8L3NjcmlwdD4="></iframe>
<div style="width: expression(alert('XSS via CSS'));"></div>
// userInput에 <img src=x onerror=...> 같은 문자열이 들어올 때
container.innerHTML = userInput;
화이트 리스트
방식 이용implementation("com.googlecode.owasp-java-html-sanitizer:owasp-java-html-sanitizer:20240325.1") // Sanitizer
@Service
public class HtmlSanitizerService {
// 1) 허용할 태그, 속성, URL 스킴을 화이트리스트로 정의
private static final PolicyFactory POLICY = new HtmlPolicyBuilder()
// 블록 요소
.allowElements("p", "div", "ul", "ol", "li", "h1", "h2", "h3", "blockquote")
// 인라인 요소
.allowElements("span", "strong", "em", "br", "code")
// 링크와 이미지
.allowElements("a", "img")
.allowAttributes("href").onElements("a")
.allowAttributes("src", "alt", "width", "height").onElements("img")
// href/src 스킴 제한
.allowUrlProtocols("http", "https")
// 스타일 속성(필요 시)
.allowAttributes("style").onElements("span", "p", "div")
.toFactory();
/**
* @param rawHtml 사용자로부터 넘어온 원시 HTML
* @return 안전하게 필터링된 HTML
*/
public String sanitize(String rawHtml) {
return POLICY.sanitize(rawHtml);
}
}
public record ReviewWriteRequest(
@Schema(description = "이번달 회고록 제목", example = "4월 총 사용 회고록")
@NotBlank(message = "제목은 필수 항목입니다.")
String title,
@Schema(description = "이번달 회고록 내용", example = "<p> 병원비.. 눈물.. </p>")
@NotBlank(message = "본문 내용은 필수 항목입니다.")
String content
) {
}
@PostMapping("/review")
@Operation(summary = "한달 회고 작성", description = "한달동안 회고를 작성합니다.")
public ResponseEntity<String> reviewWrite(@AuthenticationPrincipal CustomUserDetails userDetails,
@RequestBody @Valid ReviewWriteRequest reviewWriteRequest) {
return ResponseEntity.ok(expenditureService.writeReview(userDetails.getMember(), reviewWriteRequest));
}
@Transactional
public String writeReview(Member member, ReviewWriteRequest reviewWriteRequest) {
// 1. 혹시 모를 XSS 공격 대비 화이트리스트로 위험 요소 내용 삭제
String sanitizeContent = htmlSanitizerService.sanitize(reviewWriteRequest.content());
// 2. DB 저장인데 return으로 대체
return sanitizeContent;
}