2
0

More than 1 year has passed since last update.

クロスサイトリクエストフォージェリ(CSRF)のテスト

Last updated at Posted at 2022-06-20

セキュリティを学習するなら自分で自身を攻撃し、自ら防いで学べ

構成:Spring Boot,Thymeleaf

まずは、呟き画面を作って、urlが含まれていればurl化にするアプリを作る

image.png


image.png

コントローラ.java
	/**
	 * CSRFのテスト
	 * @param model
	 * @param formList
	 * @return
	 */
	@GetMapping("/twi")
	public String twiA(Model model, @RequestParam(name = "formList", required = false, defaultValue = "") List<String> formList) {
		List<String> list = new ArrayList<>();
		list.addAll(formList);
		model.addAttribute("formList", list);
		return "twi";
	}

<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="UTF-8" />
<title>Hello</title>
</head>

<body>
	<h1>呟きリスト</h1>
	<form action="twi">
		<span><input type="text" name="formList"></span>
		<button type="submit">呟く</button><br>
		<th:block th:each="list ,stat :${formList}">
			<input type="hidden" th:value="${list}" name="formList">
			<p th:id="${stat.count}">[[${list}]]</p>
		</th:block>
	</form>

<script>
//URL化
	const twi = document.getElementsByName('formList');
	for(let i = 0; i < twi.length ; i++){
		//'https://~.com~'をURLとみなす。
		const pattern1 = /^(https|http)(:\/\/[a-z]{1,}\.com.*)/g;
		const result1 = pattern1.test(twi[i].value);
		if (result1) {
			//要素1の中にあるURLをリンクに置き換える
			const text = document.getElementsByName('formList')[i].value;
			const url = text.replace(pattern1, "<a href='$1$2' target='_blank'>$1$2</a>");
			document.getElementById(i).innerHTML = url;
		}
	}
</script>
</body>
</html>

参考:[JavaScript]サブドメイン許可 url 正規表現 チェック

セッション認証が組み込まれている想定で攻撃

攻撃者が乗っ取りスパムリンクを投稿
image.png

ログインユーザがリンクを押下したら勝手に投稿された。
セッションIDでチェックしていても本人のセッション情報なので意味がない。
image.png

コントローラ.java
	@Autowired
	HttpSession session;

	/**
	 * CSRFのテスト
	 * 
	 * @param model
	 * @param formList
	 * @return
	 */
	@GetMapping("/twi")
	public String twiA(Model model,
			@RequestParam(name = "formList", required = false, defaultValue = "") List<String> formList,
			HttpServletRequest request) {
		session = request.getSession();
		
		//session認証 sessionIdが違っていれば呟けない処理がここにあるとする
		//sessionチェック・・・
		
		List<String> list = new ArrayList<>();
		list.addAll(formList);
		model.addAttribute("formList", list);
		return "twi";
	}
}
twi.html
<!doctype html>
<html xmlns:th="http://www.thymeleaf.org">

<head>
<meta charset="UTF-8" />
<title>Hello</title>
</head>

<body>
	<h1>呟きリスト</h1>
	<p>id:[[${#httpSession.id}]]</p>
	<p>コピペ用:http://localhost:8080/twi?formList=乗っ取り</p>
	<form action="twi">
		<span><input type="text" name="formList"></span>
		<button type="submit">呟く</button><br>
		<th:block th:each="list ,stat :${formList}">
			<input type="hidden" th:value="${list}" name="formList">
			<p th:id="${stat.count}">[[${list}]]</p>
		</th:block>
	</form>

<script>
//URL化
	const twi = document.getElementsByName('formList');
	for(let i = 0; i < twi.length ; i++){
		//'https://~'をURLとみなす。
		const pattern1 = /^(https|http)(:\/\/[a-z]{1,}.*)/g;
		const result1 = pattern1.test(twi[i].value);
		if (result1) {
			//要素1の中にあるURLをリンクに置き換える
			const text = document.getElementsByName('formList')[i].value;
			const url = text.replace(pattern1, "<a href='$1$2' target='_blank'>$1$2</a>");
			document.getElementById(i).innerHTML = url;
		}
	}
</script>
</body>
</html>

CSRF対策をする

攻撃者に予測困難なhiddenパラメータでチェックする。
ユーザーにhiddenパラメータを持たせる→投稿→サーバー側でhiddenパラメータが変更されてないかチェック。
スパムリンク押下だとhiddenパラメータが送信されてこない。もちろん画面を覗かれれば終わるが、それは以前の問題。

image.png

攻撃者が乗っ取りスパムリンクを投稿

image.png

トークンチェックにより弾かれる。

image.png

		//session認証 sessionIdが違っていれば呟けない処理がここにあるとする
		//sessionチェック・・・
		
		List<String> list = new ArrayList<>();
		
		//呟きがある場合、トークンが一致しないと呟かない
+		if(!formList.isEmpty()) {
+			if(token == 9999) {
				list.addAll(formList);
				model.addAttribute("formList", list);
+			} else {
+				list.add("トークンチェックにより呟けませんでした");
+				model.addAttribute("formList", list);
+			}
+		}
+		model.addAttribute("token", 9999);
		return "twi";
	}
<body>
	<h1>呟きリスト</h1>
	<p>id:[[${#httpSession.id}]]</p>
	<p>コピペ用:http://localhost:8080/twi?formList=乗っ取り</p>
+	<p>token:[[${token}]]</p>
	<form action="twi">
		<span><input type="text" name="formList"></span>
		<button type="submit">呟く</button><br>
		<th:block th:each="list ,stat :${formList}">
			<input type="hidden" th:value="${list}" name="formList">
			<p th:id="${stat.count}">[[${list}]]</p>
		</th:block>
+		<input type="hidden" th:value="${token}" name="token">
	</form>

参考:セッション

参考:Springにおけるセッション管理3パターン

参考:Springにおけるセッション管理(主に@SessionAttributes使用パターン)


参考:Spring SecurityでCSRF対策

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0