The FormProtection Component provides protection against form data tampering.
Like all components it is configured through several configurable parameters.
All of these properties can be set directly or through setter methods of the
same name in your controller’s initialize()
or beforeFilter()
methods.
If you are using other components that process form data in their startup()
callbacks, be sure to place FormProtection Component before those components
in your initialize()
method.
Note
When using the FormProtection Component you must use the FormHelper to create
your forms. In addition, you must not override any of the fields’ “name”
attributes. The FormProtection Component looks for certain indicators that are
created and managed by the FormHelper (especially those created in
create()
and
end()
). Dynamically altering
the fields that are submitted in a POST request, such as disabling, deleting
or creating new fields via JavaScript, is likely to cause the form token
validation to fail.
By default the FormProtectionComponent
prevents users from tampering with
forms in specific ways. It will prevent the following things:
Form’s action (URL) cannot be modified.
Unknown fields cannot be added to the form.
Fields cannot be removed from the form.
Values in hidden inputs cannot be modified.
Preventing these types of tampering is accomplished by working with the FormHelper
and tracking which fields are in a form. The values for hidden fields are
tracked as well. All of this data is combined and turned into a hash and hidden
token fields are automatically be inserted into forms. When a form is submitted,
the FormProtectionComponent
will use the POST data to build the same structure
and compare the hash.
Note
The FormProtectionComponent will not prevent select options from being added/changed. Nor will it prevent radio options from being added/changed.
Configuring the security component is generally done in the controller’s
initialize()
or beforeFilter()
callbacks
Available options are:
Set to false
to completely skip the validation of POST
requests, essentially turning off form validation.
Set to a list of form fields to exclude from POST validation. Fields can be
unlocked either in the Component, or with
FormHelper::unlockField()
. Fields that have been unlocked are
not required to be part of the POST and hidden unlocked fields do not have
their values checked.
Actions to exclude from POST validation checks.
Callback to call in case of validation failure. Must be a valid Closure. Unset by default in which case exception is thrown on validation failure.
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\EventInterface;
class WidgetsController extends AppController
{
public function initialize(): void
{
parent::initialize();
$this->loadComponent('FormProtection');
}
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
if ($this->request->getParam('prefix') === 'Admin') {
$this->FormProtection->setConfig('validate', false);
}
}
}
The above example would disable form tampering prevention for admin prefixed routes.
There may be cases where you want to disable form tampering prevention for an
action (ex. AJAX requests). You may “unlock” these actions by listing them in
$this->Security->unlockedActions
in your beforeFilter()
:
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\EventInterface;
class WidgetController extends AppController
{
public function initialize(): void
{
parent::initialize();
$this->loadComponent('FormProtection');
}
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
$this->FormProtection->setConfig('unlockedActions', ['edit']);
}
}
This example would disable all security checks for the edit action.
If form protection validation fails it will result in a 400 error by default.
You can configure this behavior by setting the validationFailureCallback
configuration option to a callback function in the controller.
By configuring a callback method you can customize how the failure handling process works:
public function beforeFilter(EventInterface $event)
{
parent::beforeFilter($event);
$this->FormProtection->setConfig(
'validationFailureCallback',
function (BadRequestException $exception) {
// You can either return a response instance or throw the exception
// received as argument.
}
);
}