質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.37%
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

CSRF

クロスサイトリクエストフォージェリ (Cross site request forgeries、CSRF)は、 外部Webページから、HTTPリクエストによって、 Webサイトの機能の一部が実行されてしまうWWWにおける攻撃手法です。

Q&A

1回答

6334閲覧

Cakephp4でREST APIを実装するとCSRFエラーが発生

yukie1101

総合スコア11

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

CakePHP

CakePHPは、PHPで書かれたWebアプリケーション開発用のフレームワークです。 Ruby on Railsの考え方を多く取り入れており、Railsの高速性とPHPの機動性を兼ね備えています。 MVCやORMなどを「規約優先の考え方」で利用するため、コードを書く手間を省くことができます。 外部のライブラリに依存しないので、単体での利用が可能です。

CSRF

クロスサイトリクエストフォージェリ (Cross site request forgeries、CSRF)は、 外部Webページから、HTTPリクエストによって、 Webサイトの機能の一部が実行されてしまうWWWにおける攻撃手法です。

0グッド

1クリップ

投稿2021/08/16 03:43

前提・実現したいこと

CakePHP4を使って、APIを実装したいと考えています。
API実装部分以外はログイン機能のあるシステムで、外部フォームから入力されたデータを
API機能を使ってデータベースに取り込み、システム内でデータを利用するという前提です。

発生している問題・エラーメッセージ

API以外の部分はログイン機能を実装しているため、元々はCakePHP全体にCSRF設定の有効化をしていましたが
Cookbookを参考にしつつ、API部分のみCSRFを無効化しようとしても、反映されません。
単純にAPIを提供しているURLを叩くとデータは表示されますが、別サーバーからcURL等でアクセスすると
以下のエラーが表示される、という状態です。

Missing or incorrect CSRF cookie type.

該当のソースコード

// routes.php // API $routes->scope('/api', ['prefix' => 'Api'], function (RouteBuilder $builder) { $builder->registerMiddleware('bodies', new BodyParserMiddleware()); $builder->applyMiddleware('bodies'); $builder->setExtensions(['json']); // Forms $builder->connect('/forms/search', ['controller' => 'Forms', 'action' => 'search'])->setMethods(['GET']); $builder->connect('/forms/create', ['controller' => 'Forms', 'action' => 'create'])->setMethods(['POST']); });
// Application.php public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue { $middlewareQueue // Catch any exceptions in the lower layers, // and make an error page/response ->add(new ErrorHandlerMiddleware(Configure::read('Error'))) // Handle plugin/theme assets like CakePHP normally does. ->add(new AssetMiddleware([ 'cacheTime' => Configure::read('Asset.cacheTime'), ])) // Add routing middleware. // If you have a large number of routes connected, turning on routes // caching in production could improve performance. For that when // creating the middleware instance specify the cache config name by // using it's second constructor argument: // `new RoutingMiddleware($this, '_cake_routes_')` ->add(new RoutingMiddleware($this)) // Parse various types of encoded request bodies so that they are // available as array through $request->getData() // https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware ->add(new BodyParserMiddleware()) // Cross Site Request Forgery (CSRF) Protection Middleware // https://book.cakephp.org/4/en/controllers/middleware.html#cross-site-request-forgery-csrf-middleware // ->add(new CsrfProtectionMiddleware([ // 'httponly' => true, // ])) ->add(new RoutingMiddleware($this)) // RoutingMiddleware の後に認証を追加 ->add(new AuthenticationMiddleware($this)); // CSRFに関する設定 $csrf = new CsrfProtectionMiddleware([ 'httponly' => true, ]); // コールバックが `true` を返す場合、トークンのチェックはスキップされます。 $csrf->skipCheckCallback(function ($request) { // Skip token check for API URLs. if ($request->getParam('prefix') === 'Api') { return true; } }); $middlewareQueue->add($csrf); return $middlewareQueue; }
// src/Controller/Api/FormsController.php <?php declare(strict_types=1); namespace App\Controller\Api; use App\Controller\AppController; use Cake\Cache\Cache; use Cake\Event\EventInterface; class FormsController extends AppController { public function initialize(): void { parent::initialize(); // TODO: Change the autogenerated stub $this->loadComponent('RequestHandler'); } /** * テスト用API(データ取得) */ public function search() { $forms = $this->Forms->find('all'); $this->set(compact('forms')); $this->viewBuilder()->setOption('serialize', ['forms']); } /** * データ登録API */ public function create() { $result = $this->request->getData(); $this->set(compact('result')); $this->viewBuilder()->setOption('serialize', ['result']); } public function beforeFilter(EventInterface $event) { parent::beforeFilter($event); $this->Authentication->allowUnauthenticated(['create', 'search']); } }

試したこと

エラーの内容としてはCSRFが無効化されていない状態のようだったので、
Application.phpの中のif文を$request->getParam('controller') === 'Forms'に変更するなど
してみましたが、同様のエラーが出て回避はできませんでした。

補足情報(FW/ツールのバージョンなど)

※各種バージョン情報※
CakePHPバージョン:4.2.8
PHPバージョン:7.4.16
開発環境:MAMP 6.4

※参考サイト※
CakePHP 4.x Strawberry Cookbook CMS チュートリアル - 認証
CakePHP 4.x Strawberry Cookbook クロスサイトリクエストフォージェリー (CSRF) ミドルウェア
CakePHP4 で簡単な API を作る
CakePHP 4 で CSRF 保護を一部または完全に無効化(有効化)する方法

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

自己解決というか、時間がなかったので正当な方法に修正して今回は実装を決定しました。

やったこと

■FormsControllerをApiControllerに名称変更し、格納先をController直下に変更
元々はControllerの下にApiというディレクトリを作成し、そこにFormsControllerを格納していました。
(その方が、ApiっぽいURLでアクセスできるため)
しかし、今回はそういうイレギュラーなことをするのではなく、素直にController直下に問題のControllerクラスを格納し、名称を「ApiController」に変更しました。

PHP

1// src/Controller/FormsController.php 2<?php 3declare(strict_types=1); 4 5namespace App\Controller; 6 7use App\Controller\AppController; 8use Cake\Cache\Cache; 9use Cake\Event\EventInterface; 10 11class FormsController extends AppController { 12 public function initialize(): void 13 { 14 parent::initialize(); // TODO: Change the autogenerated stub 15 $this->loadComponent('RequestHandler'); 16 } 17 18 /** 19 * テスト用API(データ取得) 20 */ 21 public function index() { 22 $forms = $this->Forms->find('all'); 23 $this->set(compact('forms')); 24 $this->viewBuilder()->setOption('serialize', ['forms']); 25 } 26 27 /** 28 * データ登録API 29 */ 30 public function create() { 31 $result = $this->request->getData(); 32 $this->set(compact('result')); 33 $this->viewBuilder()->setOption('serialize', ['result']); 34 } 35}

■メソッド名をCakePHPに準拠
こちらも、素直にCakePHPの仕様に従って、一覧を取得するメソッド名を「search」→「index」に変更しました。

■routes.phpの書き換え
当初、Controllerディレクトリの下にApiディレクトリを作成し、その下にControllerクラスを置いていたこと、
またindexの代わりにsearchメソッドを使用していたことから、 以下のような記述をしていました。

PHP

1// routes.php 2// API 3$routes->scope('/api', ['prefix' => 'Api'], function (RouteBuilder $builder) { 4 $builder->registerMiddleware('bodies', new BodyParserMiddleware()); 5 $builder->applyMiddleware('bodies'); 6 7 $builder->setExtensions(['json']); 8 9 // Forms 10 $builder->connect('/forms/search', ['controller' => 'Forms', 'action' => 'search'])->setMethods(['GET']); 11 $builder->connect('/forms/create', ['controller' => 'Forms', 'action' => 'create'])->setMethods(['POST']); 12});

上記を全てコメントアウトし、素直な書き方でjsonが提供できる形に修正しました。

PHP

1$routes->scope('/', function (RouteBuilder $builder) { 2 /* 3 * Here, we are connecting '/' (base path) to a controller called 'Pages', 4 * its action called 'display', and we pass a param to select the view file 5 * to use (in this case, templates/Pages/home.php)... 6 */ 7 $builder->connect('/', ['controller' => 'Pages', 'action' => 'display', 'home']); 8 9 /* 10 * ...and connect the rest of 'Pages' controller's URLs. 11 */ 12 $builder->connect('/pages/*', 'Pages::display'); 13 // ここにApi提供用の記述を追加 14 $builder->setExtensions(['json']); 15 $builder->resources('Api'); 16 17 18 /* 19 * Connect catchall routes for all controllers. 20 * 21 * The `fallbacks` method is a shortcut for 22 * 23 * ``` 24 * $builder->connect('/:controller', ['action' => 'index']); 25 * $builder->connect('/:controller/:action/*', []); 26 * ``` 27 * 28 * You can remove these routes once you've connected the 29 * routes you want in your application. 30 */ 31 $builder->fallbacks(); 32});

■CSRFに関するチェックのスキップ処理条件を修正
最後に、Application.phpに記載していたCSRFの処理をスキップさせる条件を
getParam('prefix')からgetParam('controller')に修正しました。

PHP

1// Application.php 2 public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue 3 { 4 $middlewareQueue 5 // Catch any exceptions in the lower layers, 6 // and make an error page/response 7 ->add(new ErrorHandlerMiddleware(Configure::read('Error'))) 8 9 // Handle plugin/theme assets like CakePHP normally does. 10 ->add(new AssetMiddleware([ 11 'cacheTime' => Configure::read('Asset.cacheTime'), 12 ])) 13 14 // Add routing middleware. 15 // If you have a large number of routes connected, turning on routes 16 // caching in production could improve performance. For that when 17 // creating the middleware instance specify the cache config name by 18 // using it's second constructor argument: 19 // `new RoutingMiddleware($this, '_cake_routes_')` 20 ->add(new RoutingMiddleware($this)) 21 22 // Parse various types of encoded request bodies so that they are 23 // available as array through $request->getData() 24 // https://book.cakephp.org/4/en/controllers/middleware.html#body-parser-middleware 25 ->add(new BodyParserMiddleware()) 26 27 // Cross Site Request Forgery (CSRF) Protection Middleware 28 // https://book.cakephp.org/4/en/controllers/middleware.html#cross-site-request-forgery-csrf-middleware 29// ->add(new CsrfProtectionMiddleware([ 30// 'httponly' => true, 31// ])) 32 ->add(new RoutingMiddleware($this)) 33 // RoutingMiddleware の後に認証を追加 34 ->add(new AuthenticationMiddleware($this)); 35 36 // CSRFに関する設定 37 $csrf = new CsrfProtectionMiddleware([ 38 'httponly' => true, 39 ]); 40 // コールバックが `true` を返す場合、トークンのチェックはスキップされます。 41 $csrf->skipCheckCallback(function ($request) { 42 // Skip token check for API URLs. 43 if ($request->getParam('controller') === 'Api') { 44 return true; 45 } 46 }); 47 $middlewareQueue->add($csrf); 48 49 return $middlewareQueue; 50 }

こちらで、ひとまず正常に動作できました!
データ取得、データ登録共に問題なく動いています。

ただ、今後のことを考え、いずれは元々の構想通りでAPI処理を実装できたらな、とは思っています。
引き続き空いている時間で調査は続けていこうと思います。

皆様、ありがとうございました!

投稿2021/08/17 02:00

yukie1101

総合スコア11

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.37%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問