java 에서 jsp 로 값을 동적으로 넘겨서 화면을 구성하는 경우 newline('\n') 에 대한 처리가 필요할 때가 있다.
일반적으로 Window 계열에서는 CRLF('\r\n')
으로 라인 분리를 하고, MAC 계열에서는 CR('\n')
로 분리를 하게 된다.
다만 여기서 문제는
jsp
, html
에서는 \r\n 또는 \n 으로 라인 분리가 되지 않는다.이를 해결하기 위해 다음과 같은 방법을 사용할 수 있다.
java
@RestController
public class TestController{
@GetMapping("/")
public String getPage(Model model){
String contents = "test1\r\ntest2\r\n";
contents = contents.replaceAll("\\r?\\n", "<br/>"));
//\r\n 또는 \n 에 대해서 <br/> 로 변화
model.addAttribute("contents",contents);
return "test";
}
}
jsp
...
<span>${contents}</span>
...
결과
test1
test2
다만, 한가지 문제가 있다. Xss 공격에 대해 취약하다는 것이다.
예를 들어, contents="<script>alert(document.cookie)</script>
" 의 경우 해당 페이지에 접근하게 되면, cookie 가 노출되게 된다.
따라서 이를 해결하기 위해 <c:out> tag 를 사용한다.
jsp
...
<span><c:out value="${contents}"/></span>
...
결과
test1<br/>test2<br/>
오잉??? 텍스트가 의도한 대로 표기되지 않는다. cout은 내부 tag 속성을 무시하고 text 그대로 출력하기 때문에 <script>
등은 그대로 <script>
로 표기되고 <br>
도 마찬가지 이다.
그렇다면 어떻게 이것을 해결할까?
먼저 java 단의 replaceAll 구문을 없애고 jsp 단에서 처리를 해준다.
jsp
<% pageContext.setAttribute("CRLF", "\r\n"); %>
<% pageContext.setAttribute("LF", "\n"); %>
...
<span>
${fn:replace(fn:replace(fn:escapeXml(contents), CRLF, '<br/>'), LF, '<br/>')}
</span>
...
먼저 escapeXml을 통하여 xss 공격을 방어하고, 중첩으로 fn:replace 를 사용하여 두번에 걸쳐 line breaking 을 처리한다.
결과
String contents = "test1\r\ntest2\r\n";
test1
test2
String contents = "<script>alert('test');</script>
";
<script>alert('test');</script>