クロスサイト・リクエスト・フォージェリー対応方法
step1:
各ページでAPIを呼び出す直前にワンタイムトークン(乱数Aとする)を生成し、生成した「乱数A」をCookieに格納します。
「乱数A」をハッシュ化(SHA256)します。その結果を「ハッシュB」とします。
utils.tsに共通方法を追加する
//クロスサイト・リクエスト・フォージェリーの対策:ランダム文字列ハッシュ化(SHA256)
export const digestMessage = async (message: string | undefined) => {
const msgUint8 = new TextEncoder().encode(message); // encode as (utf-8) Uint8Array
const hashBuffer = await crypto.subtle.digest('SHA-256', msgUint8); // hash the message
const hashArray = Array.from(new Uint8Array(hashBuffer)); // convert buffer to byte array
const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); // convert bytes to hex string
return hashHex;
}
//SHA256化Tokenを生成する
export const setCsrfToken = async () => {
var radm = String(Math.random());
console.log("SHA256化前:", radm);
const csrf_token = await digestMessage(radm);
console.log("SHA256化後:", csrf_token);
console.log('set csrf_token=' + csrf_token);
document.cookie="csrf_token=" + csrf_token + "; path=/";
};
//Tokenを取得する
export const getCsrfToken = () => {
const csrf_token = document.cookie.replace(/(?:(?:^|.;\s)csrf_token\s*=\s*([^;]).$)|^.*$/, "");
console.log('get csrf_token=' + csrf_token);
return csrf_token;
};
step 2:
「ハッシュB」はAPIのパラメーターとして、サーバーへ送信します。その際、Cookieに埋め込まれた「乱数A」の値も送信されます。
// ページの初期化後にホームの情報を取得する
useEffect(() => {
setCsrfToken();
dispatch({
type: 'home/fetchHomeInfo',
payload: {
language: getLocale(),
csrfToken: getCsrfToken(),
}
}).then((res: RequestResponse) => requestErrorPopup(res));
}, []);
step3:
API側でCookieに格納された「乱数A」とリクエストとして送られてきた「ハッシュB」を取り出して、取り出した「乱数A」をハッシュ化(SHA256)して「ハッシュB」と比較します。
Cookie cookie = request.getCookie("csrf_token").get();
if(!csrfToken.equals(cookie.value())) {
//if(!csrfToken.equals(cookie.value())) {
logger__.error("csrfToken is diffent with request cooike");
return internalServerError(LogMessage.CMN_SYSTEM_ERROR);
}