Mark Shust

Mark Shust

Bypass CSRF form validation for Magento POST submissions


Magento implements CSRF (Cross-Site Request Forgery) tokens on form POST submissions. This is designed to prevent unwanted actions from being executed while a user is currently authenticated.

In certain specific situations, you will need to disable CSRF for specific routes. For example, I recently stumbled across a situation when implementing Cybersource Secure Acceptance as a payment method using the Silent Direct Post payment processing implementation. The module had not yet been updated for Magento 2.3, and when submitting a credit card order, Cybersource would post back to Magento and the order submission would fail due to an “Invalid Form Key” error response, even though the Cybersource side of things was successful.

Whenever you come across an “Invalid Form Key” error, it typically means the CSRF token has either expired, or the token was incorrectly implemented. In this situation, Cybersource was posting back to Magento and there was no CSRF token in place, which would always lead to an error being thrown. The resolution is to disable CSRF, but just for this specific endpoint (/cybersource/index/placeOrder).

Instead of modifying the controller directly, I created a local module override for the Cybersource module. First I create a Foo_SecureAcceptance module (a good way to name a module overriding the Cybersource_SecureAcceptance module):

Foo/SecureAcceptance/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Foo_SecureAcceptance',
    __DIR__
);
Foo/SecureAcceptance/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Foo_SecureAcceptance" setup_version="1.0.0">
        <sequence>
            <module name="CyberSource_SecureAcceptance"/>
        </sequence>
    </module>
</config>

This is a simple module, using a class preference to override the PlaceOrder controller of the Cybersource Secure Acceptance module:

Foo/SecureAcceptance/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="CyberSource\SecureAcceptance\Controller\Index\PlaceOrder" type="Foo\SecureAcceptance\Controller\Index\PlaceOrder"/>
</config>

For this class preference, I wanted to extend the existing PlaceOrder class. The difference with my controller being that it is implementing the CsrfAwareActionInterface interface. Implementing this interface requires the defining of two methods:

Foo/SecureAcceptance/Controller/Index/PlaceOrder.php
<?php
namespace Foo\SecureAcceptance\Controller\Index;

use Magento\Framework\App\CsrfAwareActionInterface;
use Magento\Framework\App\Request\InvalidRequestException;
use Magento\Framework\App\RequestInterface;

class PlaceOrder extends \CyberSource\SecureAcceptance\Controller\Index\PlaceOrder implements CsrfAwareActionInterface
{
    /**
     * @inheritDoc
     */
    public function createCsrfValidationException(
        RequestInterface $request
    ): ?InvalidRequestException {
        return null;
    }

    /**
     * @inheritDoc
     */
    public function validateForCsrf(RequestInterface $request): ?bool
    {
        return true;
    }
}

By returning null for createCsrfValidationException, and true for validateForCsrf, no exception will be thrown, and the request will automatically confirm the CSRF validation for POST submissions.