CSRF対策¶
項目
概要¶
Cross-Site Request Forgery(以下 CSRF)対策として、 intra-mart Accel Platform では imSecureTokenタグ を用意しています。 このタグにより生成されるトークンに対してサーバ側でトークンチェック処理を行うことにより、CSRF対策を行います。 ここでは、CSRF対策を実施したいメソッドにAOPを使ってトークンチェック処理を織り込む方法について説明します。
imSecureTokenタグを使ったCSRF対策¶
imSecureTokenタグでは、CSRF対策の方法としてトークンを使います。 jspのformにimSecureTokenタグを記述する、ajaxで送信するデータにトークンをセットするなどをして、 サーバに送信するリクエストにトークンをセットします。 サーバ側でトークンチェック処理を実装し、リクエストのトークンの検証を行い、CSRF対策を行います。
imSecureTokenタグでは、トークンチェック処理について2つの方法を紹介しています。
- token-filtering-target-configのxmlに設定を記述し、トークンチェック処理を自動で行う方法
- ユーザでトークンチェック処理を実装する方法
ここでは、後者の「ユーザでトークンチェック処理を実装する方法」をAOPを使って実行する方法を紹介します。
AOPを使ってトークンチェック処理を実行する方法¶
ここでは単純な登録処理を例にCSRF対策の方法を説明します。
対象となるファイルは以下の通りです。
- 入力フォームのjsp
- 登録処理のControllerクラス
- AOPを設定するjavaクラス
- bean定義xmlファイル
- applicationContext-im_tgfw_common.xml (2014 Winter(Iceberg)の変更点の確認)
- applicationContext-im_tgfw_web.xml (2014 Winter(Iceberg)の変更点の確認)
注意
ここでは、 intra-mart Accel Platform 2014 Winter(Iceberg) で導入された SecureTokenValidator、SecureTokenExceptionを利用しています。 お使いの intra-mart Accel Platform が 2014 Winter(Iceberg) 以上であることを確認してください。
入力フォームのjsp¶
登録する入力フォームに imSecureTokenタグを追記します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | <%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="imui" uri="http://www.intra-mart.co.jp/taglib/imui"%>
<%@ taglib prefix="imst" uri="http://www.intra-mart.co.jp/taglib/imst" %>
<imui:head>
<title>something creation</title>
</imui:head>
<div class="imui-title">
<h1>something creation</h1>
</div>
<div class="imui-form-container-narrow">
<form id="form" action="myapp/something/creation/create" method="POST">
<!-- 入力フォームにimSecureTokenを追加します。 -->
<imst:imSecureToken />
...
</form>
</div>
|
登録処理のController¶
AOPでのPointcutの記述を簡単にするために、メソッド名やメソッド引数をパターン化しておきます。 また、トークンチェックに必要なHttpServletRequestを引数に含めます。
ここでは、登録処理のメソッド名をcreateとし、HttpServletRequestが引数の最後になるパターンにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | package sample.myapplication.app.something;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
@RequestMapping("myapp/something/creation")
public class CreationController {
/**
* 登録フォーム表示
*/
@RequestMapping(value = "create", params = "form", method = RequestMethod.GET)
public String createForm() {
return "myapp/something/createForm.jsp";
}
/**
* 登録処理
* AOP定義に合わせてメソッド名をcreateにしています。
* また、引数の最後に HttpServletRequest を追加しています。
*/
@RequestMapping(value = "create", method = RequestMethod.POST)
public String create(@ModelAttribute @Valid final SimpleForm form, final BindingResult result,
final RedirectAttributes model, final HttpServletRequest request) {
if (result.hasErrors()) {
// エラー処理
// ...
return "myapp/something/createForm.jsp";
}
// 登録処理
model.addFlashAttribute(form);
return "redirect:create?complete";
}
/**
* 登録完了画面
*/
@RequestMapping(value = "create", params = "complete", method = RequestMethod.GET)
public String createComplete(@ModelAttribute final SimpleForm form) {
return "myapp/something/createComplete.jsp";
}
@ModelAttribute
public SimpleForm setUpForm() {
final SimpleForm form = new SimpleForm();
return form;
}
}
|
AOPを設定するjavaクラス¶
ここでは汎用的なpointcut定義とCSRF対策に特化したpointcut定義の2クラスに分けています。
汎用的なpointcut定義
登録処理のメソッド名に対してpointcutを定義します。ここでは登録処理のメソッド名としてcreateとしているので、それに合わせた設定にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package sample.myapplication.csrf; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; /** * 汎用的なPointcutを定義します。 */ @Aspect public class ApplicationArchitecture { @Pointcut("execution(* sample.myapplication.app.*.*.create(..))") public void createOperation() { } @Pointcut("execution(* sample.myapplication.app.*.*.remove(..))") public void removeOperation() { } @Pointcut("execution(* sample.myapplication.app.*.*.update(..))") public void updateOperation() { } }CSRF対策のAOP定義
CSRF対策のトークチェックを実行するpointcutを定義します。メソッドの引数の最後にHttpServletRequestがあるパターンを追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package sample.myapplication.csrf; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import jp.co.intra_mart.foundation.secure_token.SecureTokenException; import jp.co.intra_mart.framework.extension.spring.web.csrf.SecureTokenValidator; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; /** * セキュアトークン検証処理を行うaspectを定義したクラスです。 */ @Aspect public class CsrfAspect { @Inject private SecureTokenValidator secureTokenValidator; /** * beforeでトークンチェック処理を実行します。 */ @Before("create(request) || update(request) || remove(request)") public void doSecureTokenCheck(final HttpServletRequest request) throws SecureTokenException { // トークンチェック処理を実行します。 secureTokenValidator.validate(request); } /** * 登録処理でCSRF対策を行うPointcutです。 * ApplicationArchitectureに定義した登録処理のPointcutに、トークンチェックのために引数の条件を追加しています。 */ @Pointcut("sample.myapplication.csrf.ApplicationArchitecture.createOperation() && args(..,request)") private void create(final HttpServletRequest request) { } /** * 削除処理でCSRF対策を行うPointcutです。 * ApplicationArchitectureに定義した削除処理のPointcutに、トークンチェックのために引数の条件を追加しています。 */ @Pointcut("sample.myapplication.csrf.ApplicationArchitecture.removeOperation() && args(..,request)") private void remove(final HttpServletRequest request) { } /** * 更新処理でCSRF対策を行うPointcutです。 * ApplicationArchitectureに定義した更新処理のPointcutに、トークンチェックのために引数の条件を追加しています。 */ @Pointcut("sample.myapplication.csrf.ApplicationArchitecture.updateOperation() && args(..,request)") private void update(final HttpServletRequest request) { } }
SpringのAOPの詳細については、「Aspect Oriented Programming with Spring」を参照してください。
bean定義xmlファイル¶
AOPを定義したjavaクラスのbean定義をbean定義xmlに記述します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- @AspectJ Support の有効化 -->
<aop:aspectj-autoproxy />
<!-- AOPの汎用的な定義 -->
<bean class="sample.myapplication.csrf.ApplicationArchitecture" />
<!-- AOPのCSRF対策用の定義 -->
<bean class="sample.myapplication.csrf.CsrfAspect" />
</beans>
|
applicationContext-im_tgfw_common.xml¶
intra-mart Accel Platformを2014 Summer(Honoka)以前から2014 Winter(Iceberg)以降へとバージョンアップした場合、 applicationContext-im_tgfw_common.xmlに、以下の変更が反映されていることを確認してください。
exceptionCodeResolverのbean定義
InvalidSecureTokenExceptionを追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <!-- Exception Code Resolver. -->
<bean id="exceptionCodeResolver"
class="org.terasoluna.gfw.common.exception.SimpleMappingExceptionCodeResolver">
<!-- Setting and Customization by project. -->
<property name="exceptionMappings">
<map>
<entry key="ResourceNotFoundException" value="w.im.fw.0001" />
<entry key="InvalidTransactionTokenException" value="w.im.fw.0004" />
<entry key="InvalidSecureTokenException" value="w.im.fw.0005" />
<entry key="BusinessException" value="w.im.fw.0002" />
</map>
</property>
<property name="defaultExceptionCode" value="e.im.fw.0001" />
</bean>
|
applicationContext-im_tgfw_web.xml¶
intra-mart Accel Platformを2014 Summer(Honoka)以前から2014 Winter(Iceberg)以降へとバージョンアップした場合、 applicationContext-im_tgfw_web.xmlに、以下の変更が反映されていることを確認してください。
secureTokenValidatorのbean定義
secureTokenValidatorのbean定義をしています。
1 2 | <!-- secure token validator -->
<bean id="secureTokenValidator" class="jp.co.intra_mart.framework.extension.spring.web.csrf.SecureTokenValidator" />
|
SystemExceptionResolverのbean定義
secureTokenErrorの設定を追加しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | <!-- Setting Exception Handling. -->
<!-- Exception Resolver. -->
<bean class="org.terasoluna.gfw.web.exception.SystemExceptionResolver">
<property name="exceptionCodeResolver" ref="exceptionCodeResolver" />
<!-- Setting and Customization by project. -->
<property name="order" value="3" />
<property name="exceptionMappings">
<map>
<entry key="ResourceNotFoundException" value="im_tgfw/common/error/resourceNotFoundError.jsp" />
<entry key="BusinessException" value="im_tgfw/common/error/businessError.jsp" />
<entry key="InvalidTransactionTokenException" value="im_tgfw/common/error/transactionTokenError.jsp" />
<entry key="InvalidSecureTokenException" value="im_tgfw/common/error/secureTokenError.jsp" />
</map>
</property>
<property name="statusCodes">
<map>
<entry key="im_tgfw/common/error/resourceNotFoundError" value="404" />
<entry key="im_tgfw/common/error/businessError" value="200" />
<entry key="im_tgfw/common/error/transactionTokenError" value="409" />
<entry key="im_tgfw/common/error/secureTokenError" value="403" />
</map>
</property>
<property name="excludedExceptions">
<array>
<value>org.springframework.web.util.NestedServletException</value>
</array>
</property>
<property name="defaultErrorView" value="im_tgfw/common/error/systemError.jsp" />
<property name="defaultStatusCode" value="500" />
</bean>
|