Invalid Form Key: why does that happen?

Have you ever come across this issue before when you were trying to access a Magento 2 admin area?

Do you know what is the Form Key and why it exists in Magento? No? I have a piece of good news for you then: you’re in the right place. Let’s discuss a little bit about the Form Key implementation in Magento 2.

What exactly is a Form Key in Magento 2?

In Magento 2, the Form Key represents a security feature used to protect the platform and the merchant from CSRF attacks. CSRF stands for Cross-Site Request Forgery and you can know more about it in this article (TBD).

CSRF attacks occur when a malicious website tricks a user’s browser into making unauthorized requests on another website where the user is authenticated.

The Form Key is automatically added to forms generated by Magento’s form-building classes, such as \Magento\Framework\Data\Form, and you can also add it manually to custom forms by using the \Magento\Framework\Data\Form\Element\FormKey class.

By including the Form Key in your forms, you enhance the security of your Magento 2 store and help prevent CSRF attacks that could manipulate or compromise user data.

Is it really necessary for my Magento 2 site?

That said, do you think it’s really necessary to use the form keys on your website? Obviously, if you don’t want attackers making requests to your website trying to pull sensitive data from it, and/or slowing down your website, the answer is a big YES.

Keeping your website secure is always a good idea from the business perspective because, once your clients understand that your website has a security breach, they will not transact anything on your website any longer, even though your prices are the best in the market. Putting your sensitive data into forms on an unsecured website is taking too much risk for that.

The more security layers you have on your systems, the better!

What if I disable it on my site?

The answer here is simple: your website will not be secure anymore. Furthermore, your website will not protect your customers and make them feel comfortable enough to place a single order in your store. Your sales rate will fall down and you have the risk of having all of your data, including the sensitive ones, leaked by an attacker that successfully got to access your system. Does it sound good? Definitely not!

Where Magento handles the Form Key Validation?

Ah, developers! You are always so curious, aren’t you? That’s fair, in the end, you’ll be the responsible ones for solving this when your clients figure out that they’re running their Magento 2 store with the Form Key disabled, right?

Alright, let’s heads-down to the code then.

Everything starts in the DI file:

app/etc/di.xml
    <virtualType name="CsrfRequestValidator" type="Magento\Framework\App\Request\CsrfValidator" />
    <virtualType name="RequestValidator" type="Magento\Framework\App\Request\CompositeValidator">
        <arguments>
            <argument name="validators" xsi:type="array">
                <item name="csrf_validator" xsi:type="object">CsrfRequestValidator</item>
                <item name="http_method_validator" xsi:type="object">
                    Magento\Framework\App\Request\HttpMethodValidator
                </item>
            </argument>
        </arguments>
    </virtualType>
    <preference for="Magento\Framework\App\Request\ValidatorInterface" type="RequestValidator" />

All of the code above is intended to build a Virtual Type called RequestValidator, which will be the concrete implementation of the \Magento\Framework\App\Request\ValidatorInterface interface.

This validator (\Magento\Framework\App\Request\ValidatorInterface) is used in the \Magento\Framework\App\FrontController, in the method processRequest. If you want to understand better what is the Front Controller and how important it is in the Magento 2 system, go on and read this article fully dedicated to talking about it.

            try {
                $this->requestValidator->validate($request, $actionInstance);
            } catch (InvalidRequestException $exception) {
                //Validation failed - processing validation results.
                $this->logger->debug(
                    sprintf('Request validation failed for action "%s"', get_class($actionInstance)),
                    ["exception" => $exception]
                );
                $result = $exception->getReplaceResult();
                if ($messages = $exception->getMessages()) {
                    foreach ($messages as $message) {
                        $this->messages->addErrorMessage($message);
                    }
                }
            }

When the method validate($request, $actionInstance) is called, it ends up executing the class \Magento\Framework\Data\Form\FormKey\Validator and calling the validate method in the class:

    /**
     * Validate form key
     *
     * @param \Magento\Framework\App\RequestInterface $request
     * @return bool
     */
    public function validate(\Magento\Framework\App\RequestInterface $request)
    {
        $formKey = $request->getParam('form_key', null);
        
        return $formKey && Security::compareStrings($formKey, $this->_formKey->getFormKey());
    }

So, if you ever need to customize something around the Form Key, this is the class you’ll need to touch. But be careful because, if you do something wrong when you customize this class the forms may stop working and the whole site will be compromised.

Well, that’s what I had to say about the Form Keys in Magento. It’s something pretty simple and may not be too hard to wrap our minds around it.

– Tiago

2 thoughts on “Invalid Form Key: why does that happen?

Leave a comment