それでは、実際に、XSS(クロスサイトスクリプティング)をJavaサーブレットで、体験してみましょう。百聞は一見に如かずといいますしね。
なお、Chromeだと、XSSがデフォルトで動きませんので、IEで実行するようにしましょう。
XSS攻撃を体験する。
<%@ page language="java" contentType="text/html; charset=UTF8" pageEncoding="UTF-8" %> <html> <head> <title>Xssサンプル</title> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> </head> <body> <form action='XssServlet' method='POST'> 名前:<input type="text" name="name" value=''> <input type="submit" value="送信"> </form> <hr> 送信した名前:<%= request.getAttribute("name") %> </body> </html>
package servlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/XssServlet") public class XssServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException{ req.setAttribute("name", ""); // クッキー設定 resp.addCookie(new Cookie("xss", "xsstest")); common(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException{ // 文字コード設定 req.setCharacterEncoding("UTF-8"); String name = req.getParameter("name"); req.setAttribute("name", name); common(req,resp); } protected void common(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException{ RequestDispatcher disp = req.getRequestDispatcher("/xss.jsp"); disp.forward(req, resp); } }
上記、ソースをTomcat等で実行すると、下記のような表示になります。
太郎と、名前のテキストボックスに入力して、「送信」ボタンを押すと下記のように表示されます。
そこで、下記のJavaScriptを名前欄に入力して、「送信」を押しましょう。
<script>alert(document.cookie);</script>
すると、下記のように、簡単にクッキーで設定した情報をJavaScriptを使用して盗み出せてしまいます。
対策
下記の文字を、表示させる箇所をエスケープする処理を追加します。
修正後のサーブレット(メソッド「escape」を追加して、エスケープしています。)
package servlet; import java.io.IOException; import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/XssServlet") public class XssServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException{ req.setAttribute("name", ""); // クッキー設定 resp.addCookie(new Cookie("xss", "xsstest")); common(req,resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException{ // 文字コード設定 req.setCharacterEncoding("UTF-8"); String name = req.getParameter("name"); req.setAttribute("name", escape(name)); common(req,resp); } protected void common(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException{ RequestDispatcher disp = req.getRequestDispatcher("/xss.jsp"); disp.forward(req, resp); } private static String escape(String val) { if (val == null) return ""; val = val.replaceAll("&", "& amp;"); val = val.replaceAll("<", "& lt;"); val = val.replaceAll(">", "& gt;"); val = val.replaceAll("\"", """); val = val.replaceAll("'", "'"); return val; } }
そうすることで、同じJavaScriptを実行しても、下記のようにタグがエスケープされるので、JavaScriptが実行されません。
また、実際の攻撃では、このように自分のクッキー情報を盗んでも意味がありませんので、攻撃用の他サイトにアクセスしたら、iframeで本記事のような脆弱性サイトを埋め込んで、クッキー情報を盗み出します。
この記事へのコメントはありません。