ど素人から毛を生やす。<延>

Ajax通信にPHP既存のセッション認証を通す

Web > PHP 2025年3月1日(最終更新:11日前)

どもです。
PHPでセッション認証を行っているサイトがあります。

しかしこのサイトにはCSRF攻撃を行う隙が存在していました。
Ajaxを使用した際にはセッション認証を行っていなかったのです。

このサイトのAjax実行時にセッション認証を行い、CSRF攻撃を防ぐことにしました。

Ajaxにトークンを送る

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="csrf-token" content="<?= $csrf_token ?>">
<title>***</title>
<script src="ajax.js"></script>
</head>

Ajaxで値を送るには、当然ですがHTML上にトークンを記載しなければなりません。
PHPのセッション認証は基本<form>内に<input type="hidden" name="csrf-token">を設置しますが、Ajaxの場合はメタタグで書くのが一般的だそう。

この際に注意するのは、JSの読込より前にメタタグを置くこと。
でないと、次の処理でトークンを回収しようと思ったらundefined!なんてことに。
冷静に考えれば当たり前ですが、これちょっとハマりかけました自分。

送信する際は既存のAjax処理全てに載せなければならないので、ajaxSetupを使います。

$.ajaxSetup({
	headers:{
		'csrf-token': $('meta[name="csrf_token"]').attr('content')
	}
});

headersを設定すると、HTTPヘッダ情報として値を送信できます。[詳細]

PHPでトークンを受け取る

さて、受信側です。

$_SERVER['HTTP_CSRF_TOKEN']

送信したパラメータは、このような指定で取り出せます。
HTTPの「CSRF_TOKEN」という意味です。専用の命名規則ではありませんので、JS側を変更すればお好みの['HTTP_○○']が取れます。

echo (checkToken($_SERVER['HTTP_CSRF_TOKEN']))? 'ok' : 'ng';

function checkToken($chk_token){
	if($chk_token === $csrf_token){
		return true;
	}else{
		return false;
	}
}

取り方が分かれば、既存の認証処理にかければ認証部分はクリア。

新しいセッショントークンを受け取る

セッション認証をクリアしたら、セッショントークンを更新しなければなりません。
それ自体は既存処理を通って終わりですが、生成した新規トークンをAjaxに返さなければ。

しかし、

echo $new_csrf_token;

では、Ajaxの処理結果に干渉してしまいます。
こいつはあくまでAjax処理を実行するための認証で、認証をするためにAjaxを実行しているわけではないのですから。

出力テキストではなく、Ajaxの戻り値にデータを載せることは可能なのか…?

手段、ありました。

header("new-csrf-token: ".$new_csrf_token);

受信側は、こう。

$(document).ajaxSuccess(function(e, xhr, options) {
	let new_csrf_token = xhr.getResponseHeader('new-csrf-token');
});

あとは、HTML側のトークン&ajax.headersを更新してやれば、無限に認証ができます。

$(document).ajaxSuccess(function(e, xhr, options) {
	let new_csrf_token = xhr.getResponseHeader('new-csrf-token');
	
	//PHPの認証用(既存のトークン用input)
	$('input[name="csrf-token"]').val(new_csrf_token);
	
	//メタタグ
	$('meta[name="csrf-token"]').attr('content', new_csrf_token);
	
	//ajax.headersの再設定
	$.ajaxSetup({
		headers:{
			'csrf-token': $('meta[name="csrf-token"]').attr('content')
		}
	});
});
この記事は役に立ちましたか?
  • _(:3」∠)_ 面白かった (0)
  • (・∀・) 参考になった (0)
  • (`・ω・´) 役に立った (0)