Spring Boot で書籍の貸出状況確認・貸出申請する Web アプリケーションを作る ( 番外編 )( Thymeleaf で textarea に入力された改行があるデータをテキスト表示する )
概要
Spring Boot でメール送信する Web アプリケーションを作る ( その15 )( Thymeleaf を利用して HTML メールを送信する2 ) において textarea に入力された改行が含まれるデータをテキスト表示する際に ${#strings.replace(naiyo, T(java.lang.System).getProperty('line.separator'), '<br />')}
と実装したのですが、よく考えてみると入力されたデータの HTML エンコード処理を全くしていないので、XSS 対策が NG なのではないかと思い調べたので、そのメモ書きです。
参照したサイト・書籍
- Thymeleaf + Spring : How to keep line break?
http://stackoverflow.com/questions/30394419/thymeleaf-spring-how-to-keep-line-break
目次
- feature/thymeleaf-textarea-memo ブランチの作成
- 以前の実装では XSS 対策上 NG であることを確認する
- 修正する&動作確認
- commit、Push、Pull Request、マージ
手順
feature/thymeleaf-textarea-memo ブランチの作成
- feature/thymeleaf-textarea-memo ブランチを作成します。
以前の実装では XSS 対策上 NG であることを確認する
動作を確認できるサンプルを作成します。
src/main/java/ksbysample/webapp/lending/web の下に textareamemo パッケージを作成します。
src/main/java/ksbysample/webapp/lending/web/textareamemo/TextareaMemoController.java を作成します。
package ksbysample.webapp.lending.web.textareamemo; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/textareamemo") public class TextareaMemoController { @RequestMapping public String index(TextareaMemoForm textareaMemoForm) { return "textareamemo/index"; } @RequestMapping("/display") public String display(TextareaMemoForm textareaMemoForm) { return "textareamemo/display"; } }
ksbysample/webapp/lending/web/textareamemo/TextareaMemoForm.java を作成します。
package ksbysample.webapp.lending.web.textareamemo; import lombok.Data; @Data public class TextareaMemoForm { private String memo; }
src/main/resources/templates の下に textareamemo ディレクトリを作成します。
src/main/resources/templates/textareamemo/index.html を作成します。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <title>Thymeleaf で textarea に入力された改行があるデータをテキスト表示する</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/> <link th:replace="common/head-cssjs"/> <style type="text/css"> <!-- .table { background-color: #ffccff; margin-bottom: 10px; } .form-group { margin-bottom: 0px; } --> </style> </head> <!-- ADD THE CLASS layout-top-nav TO REMOVE THE SIDEBAR. --> <body class="skin-blue layout-top-nav"> <div class="wrapper"> <!-- Main Header --> <div th:replace="common/mainparts :: main-header"></div> <!-- Full Width Column --> <div class="content-wrapper"> <div class="container"> <!-- Content Header (Page header) --> <section class="content-header"> <h1>Thymeleaf で textarea に入力された改行があるデータをテキスト表示する</h1> </section> <!-- Main content --> <section class="content"> <div class="row"> <div class="col-xs-12"> <!-- @thymesVar id="textareaMemoForm" type="ksbysample.webapp.lending.web.textareamemo.TextareaMemoForm" --> <form id="textareaMemoForm" method="post" action="/textareamemo/display" th:action="@{/textareamemo/display}" th:object="${textareaMemoForm}"> <table class="table table-bordered"> <colgroup> <col width="20%"/> <col width="80%"/> </colgroup> <tbody> <tr> <th>メモ</th> <td> <div class="col-xs-12"> <div class="form-group"> <div class="row"><div class="col-xs-12"> <textarea name="memo" id="memo" class="form-control input-sm" rows="5" th:field="*{memo}"> </textarea> </div></div> </div> </div> </td> </tr> </tbody> </table> <div class="text-center"> <button class="btn bg-blue js-btn-confirm"><i class="fa fa-edit"></i> 確認画面へ</button> </div> </form> </div> </div> </section> <!-- /.content --> </div> <!-- /.container --> </div> </div> <!-- ./wrapper --> <script th:replace="common/bottom-js"></script> <script type="text/javascript"> <!-- $(document).ready(function() { $('.js-btn-confirm').bind('click', function(){ $('#textareaMemoForm').submit(); return false; }); }); --> </script> </body> </html>
src/main/resources/templates/textareamemo/display.html を作成します。
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <meta http-equiv="X-UA-Compatible" content="IE=edge"/> <title>Thymeleaf で textarea に入力された改行があるデータをテキスト表示する</title> <!-- Tell the browser to be responsive to screen width --> <meta content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" name="viewport"/> <link th:replace="common/head-cssjs"/> <style type="text/css"> <!-- .table { background-color: #ffccff; margin-bottom: 10px; } .form-group { margin-bottom: 0px; } --> </style> </head> <!-- ADD THE CLASS layout-top-nav TO REMOVE THE SIDEBAR. --> <body class="skin-blue layout-top-nav"> <div class="wrapper"> <!-- Main Header --> <div th:replace="common/mainparts :: main-header"></div> <!-- Full Width Column --> <div class="content-wrapper"> <div class="container"> <!-- Content Header (Page header) --> <section class="content-header"> <h1>Thymeleaf で textarea に入力された改行があるデータをテキスト表示する</h1> </section> <!-- Main content --> <section class="content"> <div class="row"> <div class="col-xs-12"> <!-- @thymesVar id="textareaMemoForm" type="ksbysample.webapp.lending.web.textareamemo.TextareaMemoForm" --> <form id="textareaMemoForm" method="post" action="" th:object="${textareaMemoForm}"> <table class="table table-bordered"> <colgroup> <col width="20%"/> <col width="80%"/> </colgroup> <tbody> <tr> <th>メモ</th> <td th:utext="*{memo} ? ${#strings.replace(textareaMemoForm.memo, T(java.lang.System).getProperty('line.separator'), '<br />')} : ''"></td> </tr> </tbody> </table> </form> </div> </div> </section> <!-- /.content --> </div> <!-- /.container --> </div> </div> <!-- ./wrapper --> <script th:replace="common/bottom-js"></script> </body> </html>
/textareamemo の URL は認証対象外にしたいので、ksbysample/webapp/lending/config/WebSecurityConfig.java を以下のように変更します。
@Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() // 認証の対象外にしたいURLがある場合には、以下のような記述を追加します // 複数URLがある場合はantMatchersメソッドにカンマ区切りで対象URLを複数列挙します // .antMatchers("/country/**").permitAll() .antMatchers("/fonts/**").permitAll() .antMatchers("/html/**").permitAll() .antMatchers("/encode").permitAll() .antMatchers("/urllogin").permitAll() .antMatchers("/webapi/**").permitAll() .antMatchers("/springMvcMemo/**").permitAll() .antMatchers("/textareamemo/**").permitAll() .anyRequest().authenticated();
.antMatchers("/textareamemo/**").permitAll()
を追加します。
XSS対策が NG であることを確認します。
Tomcat を起動し、http://localhost:8080/textareamemo にアクセスします。
「メモ」欄に javascript を入力し「確認画面へ」ボタンをクリックします。javascript が実行されてダイアログが表示されることが確認できます。
修正する&動作確認
stackoverflow の Thymeleaf + Spring : How to keep line break? を見ると、改行コード変換前に #strings.escapeXml(...)
を実行するか、Dialect を作成すればよいようです。#strings.escapeXml(...)
の方法に修正してみます。
src/main/resources/templates/textareamemo/display.html 内の memo の出力部分を以下のように変更します。
<tbody> <tr> <th>メモ</th> <td th:utext="*{memo} ? ${#strings.replace(#strings.escapeXml(textareaMemoForm.memo), T(java.lang.System).getProperty('line.separator'), '<br />')} : ''"></td> </tr> </tbody>
ちなみに IntelliJ IDEA Ultimate Edition だと #strings.
を入力するとメソッドの一覧が表示されますね。
動作確認します。
再度 http://localhost:8080/textareamemo にアクセスした後、「メモ」欄に javascript を入力し「確認画面へ」ボタンをクリックします。
Tomcat を停止します。
commit、Push、Pull Request、マージ
commit、GitHub へ Push、feature/thymeleaf-textarea-memo -> 1.0.x へ Pull Request、1.0.x でマージ、feature/thymeleaf-textarea-memo ブランチを削除、をします。
履歴
2016/03/06
初版発行。