Handling incoming third party POST requests
By default Yii uses CSRF protection that verifies that POST requests could be made only by the same application. It enhances overall security significantly but there are cases when CSRF should be disabled i.e. when you expect incoming POST requests from a third party service.
Additionally, if third party is posting via XMLHttpRequest (browser AJAX), we need to send additional headers to allow CORS (cross-origin resource sharing).
How to do it
First of all, never disable CSRF protection altogether. If you need it to be disabled, do it for specific controller or even controller action.
Disabling CSRF for a specific controller
Disabling protection for specific controller is easy:
class MyController extends Controller
{
public $enableCsrfValidation = false;
We've added a public property enableCsrfValidation
and set it to false.
Disabling CSRF for specific controller action
In case of disabling only a single action it's a bit more code:
class MyController extends Controller
{
public function beforeAction($action)
{
if (in_array($action->id, ['incoming'])) {
$this->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}
We've implemented beforeAction
controller method. It is invoked right before an action is executed so we're
checking if executed action id matches id of the action we want to disable CSRF protection for and, if it's true,
disabling it. Note that it's important to call parent method and call it last.
Sending CORS headers
Yii has a special Cors filter that allows you sending headers required to allow CORS.
To allow AJAX requests to the whole controller you can use it like that:
class MyController extends Controller
{
public function behaviors()
{
return [
'corsFilter' => [
'class' => \yii\filters\Cors::className(),
],
];
}
In order to do it for a specific action, use the following:
class MyController extends Controller
{
public function behaviors()
{
return [
'corsFilter' => [
'class' => \yii\filters\Cors::className(),
'cors' => [],
'actions' => [
'incoming' => [
'Origin' => ['*'],
'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'],
'Access-Control-Request-Headers' => ['*'],
'Access-Control-Allow-Credentials' => null,
'Access-Control-Max-Age' => 86400,
'Access-Control-Expose-Headers' => [],
],
],
],
];
}