Laravel は PHP のフレームワークで web ページを作る際によく用いられています。 web ページに対する攻撃の一つにCSRF(クロスサイトリクエストフォージェリ)というものがあります。端的に言えばサイトを跨いで偽物のリクエストを送る攻撃です。この対策として正規のサイトでない場所から送られたリクエストは信用できないものであり、これを弾くべき、というものがあります。この対策の実装方法として cookie を用いて逐次変わるトークンをやりとりする、というものがあります。cookie はオリジン(プロトコル、ドメイン、ポート番号で定まる通信の原点)によって扱いを制限でき、cookie を用いることで決まったオリジン――信用できるサイトの中でのみの通信ができます。
HTTP Cookie の使用 – HTTP | MDN
Origin – HTTP | MDN
これはサーバサイドで cookie にトークンを生成して保存、ブラウザ等クライアントサイドで cookie からトークンを読み取り、クライアントサイドからサーバサイドにリクエストヘッダ、ボディ等にトークンの中身を埋め込んでサーバサイドに送信、サーバサイドで cookie に再生成されたトークンを保存、……と繰り返す、という処理で実装されます。サーバサイドのフレームワークでは言語問わず、この機能を持っているものが多いです。
PHP のフレームワークである Laravel でも CSRF 攻撃への防御機能が備わっています。
CSRF保護 6.x Laravel
この防御機能を用いるためにしばしばフロント側の実装では手間を強いられます。よくあるのが完全に静的なサイトで form を POST する時の処理です。
Laravel では POST で送る _token キーの中の値でも CSRF トークン(CSRF 攻撃対策に cookie を介してやり取りするトークン)を送信できます。これを Laravel デフォルトのテンプレートエンジンである blade では次の様に実装します。
<form method="POST" action="/profile"> @csrf <!-- @csrf で <input type="hidden" name="_token" value="トークン"> な要素が埋め込まれます --> ... </form>
これだけで CSRF トークンをやり取りできます。しかしセキュアはセキュアなのですが、利便性に問題があります。CSRF ではトークンを頻繁に再生成してトークンの一致不一致を見ます。このため @csrf で埋め込まれたトークンはあっという間に不正なトークンになります。複数タブでページを開いたりした時に POST して 419 エラー(Laravel 独自の HTTP レスポンスコード。CSRFトークンの不一致を示す)が返ってきたら、大体これが原因です。
こんな感じで純粋な静的ページで CSRF トークンのやり取りをするのは厄介を含むのですが、JavaScript でならば問題ありません。通信の度に都度、現在の cookie から新鮮な CSRF トークンを得られます。通信の度に何かしらの処理を挟むには通信用の関数、メソッド、ライブラリに知悉する必要がありそうなものですが JavaScript の HTTP クライアントライブラリである axios はこの辺りを上手くやってくれています(自分が知らないだけで他ライブラリでも上手くやってくれそう)。axios はデフォルトで CSRF トークンを cookie から読んでヘッダに載せています。
実装は次です。
// @see https://github.com/axios/axios/blob/9a78465a9268dcd360d7663de686709a68560d3d/lib/adapters/xhr.js#L103 // Add xsrf header // This is only done if running in a standard browser environment. // Specifically not if we're in a web worker, or react-native. if (utils.isStandardBrowserEnv()) { // Add xsrf header var xsrfValue = (config.withCredentials || isURLSameOrigin(fullPath)) && config.xsrfCookieName ? cookies.read(config.xsrfCookieName) : undefined; if (xsrfValue) { requestHeaders[config.xsrfHeaderName] = xsrfValue; } }
config.xsrfHogeHoge とある様にこのやり取りに使うキーは設定で定められています。そしてこの記事を書いている時点でのデフォルトが次です。
axios/axios: Promise based HTTP client for the browser and node.js
{ /** 省略 **/ // `xsrfCookieName` is the name of the cookie to use as a value for xsrf token xsrfCookieName: 'XSRF-TOKEN', // default // `xsrfHeaderName` is the name of the http header that carries the xsrf token value xsrfHeaderName: 'X-XSRF-TOKEN', // default
この XSRF-TOKEN と X-XSRF-TOKEN は Laravel の CSRF 攻撃対策で用いられている cookie と header のキーと同じです。
CSRF保護 6.x Laravel
このため axios と Laravel を使って通信をするならば、特別意識せずとも CSRF 攻撃への対策が自動でされます。