クロスサイトリクエストフォージェリ
クロスサイトリクエストフォージェリとは?
Cross Site Request Forgeries
Forgery:偽もの
悪意を持って作成したサイトに攻撃用スクリプトを仕込んでおき、
訪問者に対して、訪問者が意図しない操作をさせる攻撃
・ 悪意あるページはiframe(サイズ:1×1px)で隠しページを読み込み
・ 隠しページはtrue_site.jspへ、名前や電話番号等のパラメータをPOSTする処理を含む
・ 悪意あるページを表示したユーザは、気づかずにtrue_site.jspへ名前や電話番号を入力されてしまう
・ true_site.jspが認証が必要なサイトであった場合でも、
ログイン後、ログオフする前にこの攻撃を受けると(forgery.htmlを閲覧すると)、
認証をすり抜け、認証が必要なサイトに対して意図せず行ってしまう。
攻撃方法例
※悪意あるページ(forgery.html)
<html>
<body>
<!-- 訪問者に見せる画面 -->
<p>welcome</p>
<!-- 訪問者に見せない(悪意ある隠しコード) -->
<iframe src="hide_submit.html" style="width:1px; height:1px"></iframe>
</body>
</html>
※隠しページ(hide_submit.html)
<html>
<body onload="document.myform.submit()">
<form
name="myform"
method="POST"
action="http://localhost:8080/myservlet/myPack/true_site.jsp">
<!-- ↓項目が↑サイトへ自動的にPOSTされる -->
<input type="hidden" name="name" value="株式会社米良太事務所" />
<input type="hidden" name="address" value="兵庫県西宮市" />
<input type="hidden" name="tel" value="050-3695-5892" />
<input type="hidden" name="email" value="yone@office-yone.com" />
</form>
</body>
</html>
対策
@WebFilter("/*")
public class CSRF {
public class CsrfFilter implements Filter {
@Override
public void doFilter(
ServletRequest req,
ServletResponse res,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest)req;
if (request.getMethod().equals("GET")){
// GET要求時 → トークン生成
createToken(request);
}else{
// POST要求時
if (isSameToken(request))
throw new ServletException("不正なアクセス");
}
chain.doFilter(req, res);
}
// POSTされたトークンとサーバで保持しているトークンをチェック
private boolean isSameToken(HttpServletRequest request) {
HttpSession session = request.getSession();
String s_token = (String)session.getAttribute("token");
String r_token = request.getParameter("token");
// トークン無し時 = 不正
if (s_token == null ||
r_token == null ||
r_token.isEmpty()){
return false;
}
// 一致(true) or 不一致(false)
return s_token.equals(r_token);
}
// トークンを生成
private void createToken(HttpServletRequest reqest) {
MessageDigest md = null;
HttpSession session = reqest.getSession();
// セッションIDを元にハッシュ値を生成
md = MessageDigest.getInstance("MD5");
md.update(session.getId().getBytes());
// バイトデータ → 16進数 & セッション属性に格納
session.setAttribute("token", to16(md.digest()));
}
}
}
参考