Laravel Mixを使用した
Laravelは、Laravel Mixを使用して簡単にVue.jsやReact.jsなどのフロントエンドフレームワークと組み合わせて作ることができます!
特にVue.jsやSCSSの環境はデフォルトで提供されているため、npm installまたは、yarn installするだけで一瞬で環境が作れちゃうのはとても魅力的です。
今回はそのLaravel Mixを使用して、LaravelではAPIとして処理する時に使用するapi.phpについてweb.phpとの違いを調べたので記事にしたいと思います。
web.phpとapi.phpの違い
Laravelのルーティングファイルには、web.phpとapi.phpがあるのはご存知かと思います。
通常Laravelのbladeを使用して書く場合は、web.phpを使用します。
web.phpで定義したルートには、普段からCSRF保護などの機能が有効になっています。
それでは今回のようにLaravel Mixを使用して、api.phpにLaravelのAPIの処理を記述する場合は、CSRF保護などの機能はどうなのでしょうか?
気になったので調べてみました。
api.phpは外からPOSTできるの?
CSRF保護が有効なら、api.phpに定義したルートには外からPOSTできないはずなので実際に試してみました。
テスト用のエンドポイントを開ける
こちらのようにapi.phpへテスト用のエンドポイントを定義します。
routes/api.php
Route::group(['middleware' => ['api']], function () { Route::post('test', 'TestController@test'); });
送られてきた文字列にtest_okを文字列連結して返すようなサンプルの処理を書きます。
app/Http/Controllers/TestController.php
class TestController extends Controller { public function test(Request $request) { return $request['test'] . 'test_ok'; } }
PythonでPOSTしてみた
テスト用に作成したエンドポイントへPOSTするのですが、方法はPostmanなど何でも大丈夫です。
僕はPythonが楽だったので以下のように試しにPOSTしてみました。
>>> import requests >>> response = requests.post('http://127.0.0.1:8000/api/test', data={"test": "test_key"}) >>> print(response.text) test_keytest_ok
上記の結果から、デフォルトのapi.phpだと外部からもアクセスできてしまいます。
CSRFの保護は無効なようです。
web.phpの場合
web.phpはデフォルトでCSRF保護の機能が有効なはずですが、心配になってきたのでこちらも念の為外からPOSTしてみたので掲載しておきます。
テスト用のエンドポイントを開ける
先程同様にして、web.phpへテスト用のエンドポイントを定義します。
routes/web.php
Route::post('test2', 'TestController@test');
app/Http/Controllers/TestController.php
class TestController extends Controller { public function test2(Request $request) { return $request['test'] . 'test_ok'; } }
PythonでPOSTする
先程と同様、PythonでPOSTしてみます。
>>> import requests >>> response = requests.post('http://127.0.0.1:8000/test2', data={"test": "test_key"}) >>> print(response.text) . . <!doctype html> <html lang="en"> <head> <title>Page Expired</title>
web.phpでは、「Page Expired」が表示されてCSRF保護の機能がちゃんと有効になっていました。
web.phpとapi.phpのデフォルトでの動作
web.phpはCSRF保護が有効で、api.phpはCSRF保護が無効になっていることがわかりました。
改めて考えてみると当然と言えば当然ですよね。
api.phpは当然APIでの処理を書くところなので、外からアクセスできないと使えないですもんね。
それでは、web.phpとapi.phpのデフォルトでの動作の違いは、どこの設定によるものなのでしょうか。
app/Http/Kernel.phpに設定が記述されている
app/Http/Kernel.phpにweb.phpとapi.phpのそれぞれの設定がミドルウェアとして組み込まれています。
このようにデフォルトでは以下のような設定になっています。
app/Http/Kernel.php
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ 'throttle:60,1', 'bindings', ], ];
api.phpには、VerifyCsrfTokenのミドルウェアが入っていないため機能していなかったのですね。
Laravel Mixを使用したLaravelのAPIについて考える
今回のようにLaravel Mixを使用して、フロントはVue.jsやReact.jsで作成し、LaravelのAPIを作成する場合を考えます。
通常フロントとサーバーサイドのAPIを分けて作る場合は別サーバーで作成するため、CSRFでの認証はかけられず、IP制御やその他の認証で外からのアクセスを制御するかとは思います。
しかし、今回はLaravel Mixを使用しフロントとサーバーサイドのAPIを使用して作成しているため、同サーバ内であり、かつbladeも混在させて作成することも可能です。
そのためIP制御等でもいいのですが、面倒なのでapi.phpにもCSRFを有効にしたいと思ったわけです。
api.phpでもCSRFを有効にした
app/Http/Kernel.phpのapiのところにVerifyCsrfTokenを入れればCSRFが有効になるわけですが、VerifyCsrfTokenはCookieかSessionらへんの機能を使用するため、それもミドルウェアとして含めないとエラーになります。
こちらのように、とりあえずweb.phpのやつと同じように全部入れればちゃんと動きました。
app/Http/Kernel.php
protected $middlewareGroups = [ 'web' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 'api' => [ \App\Http\Middleware\EncryptCookies::class, \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, \Illuminate\Session\Middleware\StartSession::class, // \Illuminate\Session\Middleware\AuthenticateSession::class, \Illuminate\View\Middleware\ShareErrorsFromSession::class, \App\Http\Middleware\VerifyCsrfToken::class, \Illuminate\Routing\Middleware\SubstituteBindings::class, 'throttle:60,1', 'bindings', ], ];
これでVue.jsやReact.jsもすぐに使えるし、LaravelでAPIも使えるし、セキュリティ関連もとりあえずCSRFは有効になりました。
ちなみに、bladeでHTMLのheadに以下のCSRFのtokenを入れましょう!
<meta name="csrf-token" content="{{ csrf_token() }}">
フロントからはAxiosを使えば、ヘッダーにいちいち手動でCSRFを指定しなくて済むのでとても楽です。
resources/js/bootstrap.jsで自動的にAxiosの処理の中でCSRFのtokenを付与してくれているのです。
参考サイト
コメント