13

I have a custom module with a defined route as:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="custom_module" frontName="custom-module">
            <module name="Custom_Module" />
        </route>
    </router>
</config>

With previous versions of Magento both GET and POST requests would work fine to http://mywebsite.com/custom-module/controllername

After upgrading to Magento 2.3.0, GET requests still work as before, however POST requests now do not call the execute() method of the controller. Instead, they respond with a 200 OK and a response body that is the homepage html of the website.

Does this have to do with some Csrf security feature and form keys that was added in v2.3?

1
  • Anyone got this working for 2.3.5-p1? Really annoying Shipstation don't have anything.
    – Jack
    Commented Jul 12, 2020 at 21:36

6 Answers 6

24

Please check more generous solution that does not change core functionality, you can use around plugin on Validate function of Magento\Framework\App\Request\CsrfValidator class

This implementation does not break the core functionality of Magento 2.1/2.2/2.3 versions.

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">
    <type name="Magento\Framework\App\Request\CsrfValidator">
        <plugin name="csrf_validator_skip" type="Module\Vendor\Plugin\CsrfValidatorSkip" />
    </type>
</config>

CsrfValidatorSkip.php

<?php
namespace Module\Vendor\Plugin;
class CsrfValidatorSkip
{
    /**
     * @param \Magento\Framework\App\Request\CsrfValidator $subject
     * @param \Closure $proceed
     * @param \Magento\Framework\App\RequestInterface $request
     * @param \Magento\Framework\App\ActionInterface $action
     */
    public function aroundValidate(
        $subject,
        \Closure $proceed,
        $request,
        $action
    ) {
        if ($request->getModuleName() == 'Your_Module_frontName_Here') {
            return; // Skip CSRF check
        }
        $proceed($request, $action); // Proceed Magento 2 core functionalities
    }
}

Please star my Gist page at https://gist.github.com/ananth-iyer/59ecfabcbca73d6c2e3eeb986ed2f3c4 to encourage.

6
  • $request->getModuleName() always return blank Commented Jun 24, 2019 at 11:14
  • @AnanthMage2 Can you tell me where to add this? Commented Sep 17, 2019 at 11:49
  • I am facing same issue. I added one or more custom modules. So, Can I need to add plugins in each and every module? If There are already exist di.xml files so what can I do? Commented Nov 5, 2019 at 12:31
  • How can I defeat the interlocks on my microwave? Commented Jan 29, 2021 at 21:03
  • @AndreNickatina, this is not redirecting to success page
    – Manjunath
    Commented Feb 9, 2021 at 12:44
29

UPDATE: I changed the accepted answer to the one from @AnanthMage2 as it follows Magento's coding practices


Found the solution, your controller must implement CsrfAwareActionInterface and 2 of its methods:

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

class MyController extends \Magento\Framework\App\Action\Action implements CsrfAwareActionInterface
{
    public function createCsrfValidationException(RequestInterface $request): ?InvalidRequestException
    {
        return null;
    }

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

This solves the problem, but is also backwards incompatible, i.e. your module will now not work on Magento 2.2 and earlier. To make it backwards compatible, something like the following is needed:

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

if (interface_exists("Magento\Framework\App\CsrfAwareActionInterface"))
    include __DIR__ . "/MyController.m230.php";
else
    include __DIR__ . "/MyController.m220.php";

Where you would have the full and correct class declaration in each of the two files.

6
  • 1
    Working like a charm. this is what i am looking at.. thanks!!
    – Pravin
    Commented Jan 12, 2019 at 15:44
  • Check this answer for backwards compatibility: magento.stackexchange.com/a/261410/74078
    – gemig_hol
    Commented Mar 17, 2019 at 17:26
  • 2
    The first part is great and compatible with 2.3. But the implementation of backward compatibility is out of the standards and Magento recommendations. Commented Aug 15, 2019 at 5:37
  • @RicardoMartins see the accepted answer for a standards based solution
    – snez
    Commented Aug 15, 2019 at 13:01
  • where should I add this? Commented Sep 17, 2019 at 9:40
3

Implement CsrfAwareActionInterface is a solution, but it makes the code not compatible with Magento < 2.3

Here is a trick (injecting the Key to the request on the Action) that is compatible with Magento 2.X

Put it in the constructor of the Action.

        // CsrfAwareAction Magento2.3 compatibility
        if (interface_exists("\Magento\Framework\App\CsrfAwareActionInterface")) {
            $request = $this->getRequest();
            if ($request instanceof HttpRequest && $request->isPost() && empty($request->getParam('form_key'))) {
                $formKey = $this->_objectManager->get(\Magento\Framework\Data\Form\FormKey::class);
                $request->setParam('form_key', $formKey->getFormKey());
            }
        }
1
  • $this->getRequest() is null in the __construct ... I guess it's only available in the execute() method. So, it will never enter this if. Didn't work for me at least. Magento 2.3.2. Commented Aug 15, 2019 at 5:31
1

No need to change csrf, here you can see the answer. https://magento.stackexchange.com/a/263907/59683

Add headers in your request

"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest"

And POST request on controller will work as expected.

0

One could provide a compatible solution to PHP 7.1< & Mage 2.3< if they were to outsource the validator to a different class, e.g.

if (PHP_VERSION_ID < 70100) {
    class Index extends Extendable\Main {}
} else {
    class Index extends Extendable\CsrCompatible {}
}

Where Extendable\Main has the logic for execute() {} and CsrCompatible can both extend Extendable\Main and implements CsrfAwareActionInterface. E.g:

class Main extends Action 
{
     execute() {...}
}

And

class CsrCompatible extends Main implements CsrfAwareActionInterface 
{
     //interface functions
}

The 5.6-7.0 will just never go into the CsrCompatible and therefore not throw an exception when it sees the fancy ?bool code.

0

I have added the implementation suggested by AnanthMage2 but was facing issue "$request->getModuleName() always return blank" as commented by sumeet bajaj. Major issue which we were facing was order's were not getting completed within magento after order was shipped within shipstation. So for temporary fix I have added below customization's within aroundValidate function:

class CsrfValidatorSkip {
    const CONTROLLER_MODULE = '/index.php/api/auctane';
    const CONTROLLER_NAME = '/api/auctane';

    /**
     * @param CsrfValidator $subject
     * @param Closure $proceed
     * @param RequestInterface $request
     * @param ActionInterface $action
     */
    public function aroundValidate(
        $subject,
        Closure $proceed,
        $request,
        $action
    ) {
        // Skip CSRF check
        /* if ($request->getControllerModule() == self::CONTROLLER_MODULE
            && $request->getControllerName() == self::CONTROLLER_NAME) {
            return;
        }*/
        $requestUrlCforge = parse_url($_SERVER["REQUEST_URI"], PHP_URL_PATH);
        if ($requestUrlCforge == self::CONTROLLER_MODULE
            || $requestUrlCforge == self::CONTROLLER_NAME) {
            return;
        }


        // Proceed Magento 2 core functionalities
        $proceed($request, $action);
    }
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.