脳みそ焼ききれるかと思った。
参考
やること概要
CSRFは要するに、フォームにワンタイムパスを発行して送信処理時に照合することで他所からのアタックに備えろって内容。
なので、以下の作業になる。
- セッションごとに変更されるトークン(文字列)を発行
- トークンをセッションに登録する
- 隠しフィールドにトークンを設定
- フォーム送信前にセッションと隠しフィールドを照合
nonceについて
nonceは使わない。
wpにもcf7にもnonceはあるけど使わない、というか使えない。
やっていく
こう。
functions.phpに貼り付けたら完了。
//CSRF対策
global $CSRFToken;
$CSRFToken = bin2hex(random_bytes(32));
add_action( 'after_setup_theme', 'cf7_session' );
function cf7_session() {
global $CSRFToken;
session_set_cookie_params(0, '/; SameSite=strict', '', true, true);
session_start();
$_SESSION['CSRFToken'] = $CSRFToken;
session_write_close();
}
add_filter('wpcf7_form_hidden_fields', 'my_wpcf7_form_hidden_fields');
function my_wpcf7_form_hidden_fields($hidden_fields){
global $CSRFToken;
$hidden_fields['CSRFToken'] = $CSRFToken;
return $hidden_fields;
}
add_action('wpcf7_before_send_mail', 'my_wpcf7_verify_CSRFToken');
function my_wpcf7_verify_CSRFToken($WPCF7_ContactForm){
$wpcf7 = WPCF7_ContactForm :: get_current() ;
$submission = WPCF7_Submission :: get_instance() ;
if ( $submission ){
if (empty( $posted_data )) return;
$_token = filter_input(INPUT_POST, 'CSRFToken');
if(empty($_SESSION['CSRFToken']) || $_token != $_SESSION['CSRFToken'] ){
die('送信失敗');
}else{
return $WPCF7_ContactForm;
}
}
}
解説的なもの
これを入れるとサイト内のcf7全部がCSRFに対応するので、個別の作業は必要ないです。
セッションの設定、隠しフィールドの追加で同一のトークン(文字列)を引っ張る必要がある。なのでグローバル変数(global $CSRFToken;)を使用した。今になって思うのは、セッションに登録してるんだからセッションから引っ張って隠しフィールドに流し込めばグローバル変数要らなくね?ってことなんだけど、どうでしょうね。
セッション作成時にSameSiteの設定がないとそれはそれで別のセキュリティに引っかかるので注意。例ではドメイン直下から全部を対象にしてあるけど、フォーム用のディレクトリを分けてある場合はそれを指定したほうが安心感がある。環境に合わせていじる。
照合は見ての通り。
何ら問題なく完了したときの処理は「return $WPCF7_ContactForm;」で、関数の引数に返すのを忘れない。「return;」のみだとフォームが読み込みアイコン出っ放しで固まる。
不可の処理をdie()にしてるけどぶっちゃけ何となくなので正しいかどうかはわからない。だけどまあ、普通に使う分にはこっちの条件に該当しないんで表示がどうなろうが知ったこっちゃないですね。
終わってみれば30行程度のことだけど何日も掛かった。辛かった。
多分この内容をオープンにしないほうが金を稼ぐチャンスになるんだろうけど知らん。知るか。疲れた。
突破した瞬間の話
セキュリティチェックのサービスが悪いってところがある。
コード自体を組み上げてから警告が消えるまでに作業があった。
というのがname=”CSRFToken”のことで、nameが合ってないとそもそも蹴られる。ZAPをやってる人の記事を読んでたときにリストのスクショがあったのでひょっとしてと思ったらそうだった。
使ってるサービスを見返したけどそんな説明どこにもないんだよね。まじしんどい。
おまけ
ものによっては特定のnameが入ってたら良いみたいなところがある。つまり、トークンのチェック自体はスキャンされてなかったり。サービス次第だと思うけどさ。どうでしょう。
コメント