How to Prevent TojenOrders in Magento 2
TojenOrders, also known as token hijacking attacks, can be a serious issue in any eCommerce platform, including Magento 2. These attacks exploit vulnerabilities in the system, allowing malicious actors to manipulate order data, leading to financial loss and customer dissatisfaction. In this article, I’ll walk you through a solution to mitigate this risk by creating a custom Magento 2 module.
Creating a Magento Module to Prevent TojenOrders
The approach I’ll demonstrate involves building a custom Magento 2 module. This module will add layers of validation and logging, designed to detect and block suspicious activities, particularly around order and address data. Below, you’ll find a detailed guide on how to create this module.
1. Module Registration
Start by creating a module registration file to register the module in the Magento system.
// app/code/Jickson/Mage24Fix/registration.php
<?php
declare(strict_types=1);
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Jickson_Mage24Fix',
__DIR__
);
Next, define the module in the module.xml
file:
<!-- app/code/Jickson/Mage24Fix/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="Jickson_Mage24Fix" setup_version="1.0.0"/>
</config>
2. Dependency Injection Configuration
The di.xml
file will configure dependency injection and the necessary plugins to intercept and validate data.
<!-- app/code/Jickson/Mage24Fix/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">
<!-- Plugins -->
<type name="Magento\Quote\Api\BillingAddressManagementInterface">
<plugin name="jickson_billing_set_address_plugin" type="Jickson\Mage24Fix\Plugin\BillingInfoValidator"/>
</type>
<type name="Magento\Quote\Model\ShippingAddressManagementInterface">
<plugin name="jickson_shipping_set_address_plugin" type="Jickson\Mage24Fix\Plugin\ShippingInfoValidator"/>
</type>
<!-- Logger -->
<type name="Jickson\Mage24Fix\Logger\Handler">
<arguments>
<argument name="filesystem" xsi:type="object">Magento\Framework\Filesystem\Driver\File</argument>
</arguments>
</type>
<type name="Jickson\Mage24Fix\Logger\Logger">
<arguments>
<argument name="name" xsi:type="string">access_denied_logger</argument>
<argument name="handlers" xsi:type="array">
<item name="system" xsi:type="object">Jickson\Mage24Fix\Logger\Handler</item>
</argument>
</arguments>
</type>
</config>
BillingInfoValidator Class Overview
// app/code/Jickson/Mage24Fix/Plugin/BillingInfoValidator.php
<?php
namespace Jickson\Mage24Fix\Plugin;
use Jickson\Mage24Fix\Helper\Config;
use Magento\Framework\App\State;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Quote\Api\BillingAddressManagementInterface;
use Magento\Quote\Api\Data\AddressInterface;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
class BillingInfoValidator
{
private \Magento\Framework\Webapi\Rest\Request $request;
private \Magento\Framework\App\State $state;
protected Config $config;
/**
* BillingInfoValidator constructor.
*
* @param State $state
* @param Request $request
* @param Config $config
*/
public function __construct(
\Magento\Framework\App\State $state,
\Magento\Framework\Webapi\Rest\Request $request,
Config $config
) {
$this->state = $state;
$this->request = $request;
$this->config = $config;
}
/**
* @param BillingAddressManagementInterface $subject
* @param int $cartId
* @param AddressInterface $address
* @return array
* @throws AccessDeniedHttpException
*/
public function beforeAssign(BillingAddressManagementInterface $subject, $cartId, AddressInterface $address): array {
if ($this->config->isValidationEnabled() && $this->config->isAddressSaveValidationEnabled()) {
if ($this->state->getAreaCode() === \Magento\Framework\App\Area::AREA_WEBAPI_REST) {
$fields = $this->request->getBodyParams();
if (is_array($fields) && array_key_exists('address', $fields)) {
$addressFields = $fields['address'];
$this->config->validateAddressFields($addressFields,'AccessDeniedOnCheckoutBilling');
}
}
}
return [$cartId, $address];
}
}
ShippingInfoValidator Class Overview
// app/code/Jickson/Mage24Fix/Plugin/ShippingInfoValidator.php
<?php
namespace Jickson\Mage24Fix\Plugin;
use Jickson\Mage24Fix\Helper\Config;
use Magento\Framework\App\State;
use Magento\Framework\Webapi\Rest\Request;
use Magento\Quote\Api\Data\AddressInterface;
use Magento\Quote\Model\ShippingAddressManagementInterface;
class ShippingInfoValidator
{
private \Magento\Framework\Webapi\Rest\Request $request;
private \Magento\Framework\App\State $state;
protected Config $config;
/**
* ShippingInfoValidator constructor.
*
* @param State $state
* @param Request $request
* @param Config $config
*/
public function __construct(
\Magento\Framework\App\State $state,
\Magento\Framework\Webapi\Rest\Request $request,
Config $config
) {
$this->state = $state;
$this->request = $request;
$this->config = $config;
}
/**
* @param ShippingAddressManagementInterface $subject
* @param int $cartId
* @param AddressInterface $address
* @return array
* @throws AccessDeniedHttpException|\Magento\Framework\Exception\LocalizedException
*/
public function beforeAssign(ShippingAddressManagementInterface $subject, $cartId, AddressInterface $address): array {
if ($this->config->isValidationEnabled() && $this->config->isAddressSaveValidationEnabled()) {
if ($this->state->getAreaCode() === \Magento\Framework\App\Area::AREA_WEBAPI_REST) {
$fields = $this->request->getBodyParams();
if (is_array($fields) && array_key_exists('addressInformation', $fields)) {
$shippingFields = $fields['addressInformation']['shipping_address'] ?? NULL;
if (isset($shippingFields)) {
$this->config->validateAddressFields($shippingFields,'AccessDeniedOnCheckoutShipping');
}
$billingFields = $fields['addressInformation']['billing_address'] ?? NULL;
if (isset($billingFields)) {
$this->config->validateAddressFields($billingFields,'AccessDeniedOnCheckoutBilling');
}
}
}
}
return [$cartId, $address];
}
}
3. Event Observers
Use event observers to intercept critical processes like quote saving, address saving, and customer address updates. The observers will validate the input data and block any suspicious activities.
<!-- app/code/Jickson/Mage24Fix/etc/events.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<!-- Before saving or updating a quote -->
<event name="sales_quote_save_before">
<observer name="mage24fix_quote_validation_before_quote_save" instance="Jickson\Mage24Fix\Observer\QuoteSaveObserver" />
</event>
<!-- Before saving or updating a quote address -->
<event name="sales_quote_address_save_before">
<observer name="mage24fix_quote_address_validation_before_save" instance="Jickson\Mage24Fix\Observer\QuoteAddressSaveObserver" />
</event>
</config>
QuoteSaveObserverClass Overview
// app/code/Jickson/Mage24Fix/Observer/QuoteSaveObserver.php
<?php
namespace Jickson\Mage24Fix\Observer;
use Jickson\Mage24Fix\Helper\Config;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
class QuoteSaveObserver implements ObserverInterface
{
protected Config $config;
/**
* Constructor
*
* @param Config $config
*/
public function __construct(
Config $config
) {
$this->config = $config;
}
public function execute(Observer $observer)
{
if ($this->config->isValidationEnabled() && $this->config->isAddressSaveValidationEnabled()) {
$quote = $observer->getEvent()->getQuote();
if (isset($quote)) {
// Validate customer first and last name
$firstName = $quote->getCustomerFirstname();
$lastName = $quote->getCustomerLastname();
if (isset($firstName) || isset($lastName)) {
$data = [
'customer_firstname' => $firstName,
'customer_lastname' => $lastName
];
$this->config->validateAddressFields($data,'AccessDeniedOnQuoteSave');
}
}
}
}
}
QuoteAddressSaveObserver Overview
// app/code/Jickson/Mage24Fix/Observer/QuoteAddressSaveObserver.php
<?php
namespace Jickson\Mage24Fix\Observer;
use Jickson\Mage24Fix\Helper\Config;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
class QuoteAddressSaveObserver implements ObserverInterface
{
protected Config $config;
/**
* Constructor
*
* @param Config $config
*/
public function __construct(
Config $config
) {
$this->config = $config;
}
public function execute(Observer $observer)
{
if ($this->config->isValidationEnabled() && $this->config->isAddressSaveValidationEnabled()) {
$quoteAddress = $observer->getEvent()->getQuoteAddress();
if (isset($quoteAddress)) {
// Convert the address object to an array
$quoteAddressArray = [
'firstname' => $quoteAddress->getFirstname(),
'lastname' => $quoteAddress->getLastname(),
'middlename' => $quoteAddress->getMiddlename(),
'company' => $quoteAddress->getCompany(),
'street' => $quoteAddress->getStreet(),
'city' => $quoteAddress->getCity(),
'region' => $quoteAddress->getRegion(),
'postcode' => $quoteAddress->getPostcode(),
'telephone' => $quoteAddress->getTelephone(),
'fax' => $quoteAddress->getFax(),
];
$this->config->validateAddressFields($quoteAddressArray,'AccessDeniedOnQuoteAddressSave');
}
}
}
}
Frontend Event Observers.
<!-- app/code/Jickson/Mage24Fix/etc/frontend/events.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="customer_address_save_before">
<observer name="mage24fix_customer_address_validation_before_save" instance="Jickson\Mage24Fix\Observer\AddressSaveObserver" />
</event>
</config>
AddressSaveObserver Overview
// app/code/Jickson/Mage24Fix/Observer/AddressSaveObserver.php
<?php
namespace Jickson\Mage24Fix\Observer;
use Jickson\Mage24Fix\Helper\Config;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
class AddressSaveObserver implements ObserverInterface
{
protected Config $config;
/**
* Constructor
*
* @param Config $config
*/
public function __construct(
Config $config
) {
$this->config = $config;
}
/**
* Execute observer
*
* @param Observer $observer
* @throws LocalizedException
*/
public function execute(Observer $observer)
{
if ($this->config->isValidationEnabled() && $this->config->isAddressSaveValidationEnabled()) {
$address = $observer->getEvent()->getCustomerAddress();
if (isset($address)) {
// Convert the address object to an array
// $addressData = $address->toArray();
$addressData = [
'firstname' => $address->getFirstname(),
'lastname' => $address->getLastname(),
'middlename' => $address->getMiddlename(),
'company' => $address->getCompany(),
'street' => $address->getStreet(),
'city' => $address->getCity(),
'region' => $address->getRegion(),
'postcode' => $address->getPostcode(),
'telephone' => $address->getTelephone(),
'fax' => $address->getFax(),
];
// Validate all fields in the address array
$this->config->validateAddressFields($addressData,'AccessDeniedOnCustomerAddressSave');
}
}
}
}
4. Configuration Settings
Provide an admin configuration interface to enable or disable validations, manage suspicious string patterns, and configure IP exemptions.
<!-- app/code/Jickson/Mage24Fix/etc/frontend/system.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
<system>
<section id="jicksonmagefix" translate="label" type="text" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0" canRestore="0">
<label>JICKSON Magento Fix</label>
<resource>Jickson_Mage24Fix::config</resource>
<group id="settings" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="0" canRestore="1">
<label>Settings</label>
<field id="enable_validation" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable All Validation</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
</field>
<field id="address_validation_enabled" translate="label comment" type="select" sortOrder="1" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable address validation on Customer Address Save (Quote, Customer Account and Checkout including REST API)</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<depends>
<field id="enable_validation">1</field>
</depends>
</field>
<field id="use_ip_exemption" translate="label comment" type="select" sortOrder="4" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Enable IP Exemption</label>
<source_model>Magento\Config\Model\Config\Source\Yesno</source_model>
<depends>
<field id="enable_validation">1</field>
</depends>
</field>
<field id="suspicious_patterns" translate="label" type="textarea" sortOrder="5" showInDefault="1" showInWebsite="0" showInStore="0">
<label>Suspicious String Patterns</label>
<comment>Enter one pattern per line. These patterns will be used to validate address fields against suspicious content.</comment>
<depends>
<field id="enable_validation">1</field>
</depends>
</field>
<field id="exempted_ips" translate="label" type="textarea" sortOrder="6" showInDefault="1" showInWebsite="0" showInStore="0">
<label>IP Exemptions</label>
<comment>Enter IP addresses to exempt from validation, one per line.</comment>
<depends>
<field id="use_ip_exemption">1</field>
</depends>
</field>
</group>
</section>
</system>
</config>
5. Default Config and ACL
The config.xml
file is essential for defining the default values of configuration settings in your module. This file is particularly useful for setting up default values for any custom settings you introduce, such as enabling or disabling certain validations, specifying suspicious patterns, or defining IP exemptions.
Here’s how you can define these settings in the config.xml
:
<!-- app/code/Jickson/Mage24Fix/etc/frontend/config.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Store:etc/config.xsd">
<default>
<jicksonmagefix>
<settings>
<enable_validation>1</enable_validation>
<address_validation_enabled>1</address_validation_enabled>
<use_ip_exemption>0</use_ip_exemption>
<suspicious_patterns><![CDATA[
/{{.*}}/si
/\(\s*exec\s*\)/si
/\$\{.*\}/si
/base64_/
/getTemp/
/\.filter\(/
/\.addAfterFilterCallback/
/pub;curl/
/\.php/
gettemplate
base64_
afterfiltercall
.filter(
@proton.me
.php
this.getTemp
{{var
(system).Filter
_decode
cloud-cache
]]></suspicious_patterns>
<exempted_ips><![CDATA[
{your IPs here}
]]></exempted_ips>
</settings>
</jicksonmagefix>
</default>
</config>
Explanation of the Configuration:
Enable Validation:
- The
<enable_validation>
tag controls whether all the custom validations (address, order, etc.) are enabled by default. Setting this value to1
means validations are enabled. Administrators can override this in the admin panel.
Suspicious Patterns:
- The
<suspicious_patterns>
section defines a list of default patterns that the module will look for in user inputs. This can include SQL keywords, HTML tags, or any other strings that might indicate malicious intent. These patterns are checked against user inputs like order details and addresses.
IP Exemptions:
- The
<ip_exemptions>
section specifies IP addresses that are exempt from validation. This is particularly useful for internal IP addresses or trusted networks where validation may not be necessary. Each IP address is defined in a separate<ip_*>
tag.
Access Control List (ACL)
<!-- app/code/Jickson/Mage24Fix/etc/acl.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Acl/etc/acl.xsd">
<acl>
<resources>
<resource id="Magento_Backend::admin">
<resource id="Magento_Backend::stores">
<resource id="Magento_Backend::stores_settings">
<resource id="Magento_Config::config">
<resource id="Jickson_Mage24Fix::config" title="Jickson Magento Fix" sortOrder="1021" />
</resource>
</resource>
</resource>
</resource>
</resources>
</acl>
</config>
6. Helper Class for Config and Validation
The Config
helper class centralizes the logic for validation checks, making it easier to manage and extend.
// app/code/Jickson/Mage24Fix/Helper/Config.php
<?php
declare(strict_types=1);
namespace Jickson\Mage24Fix\Helper;
use Jickson\Mage24Fix\Logger\Logger;
use Magento\Framework\App\Helper\AbstractHelper;
use Magento\Framework\App\Helper\Context;
use Magento\Framework\App\RequestInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\HTTP\PhpEnvironment\RemoteAddress;
class Config extends AbstractHelper
{
private Logger $logger;
protected RemoteAddress $remoteAddress;
protected RequestInterface $request;
/**
* Data constructor.
*/
public function __construct(
Context $context,
Logger $logger,
RemoteAddress $remoteAddress,
RequestInterface $request
) {
parent::__construct($context);
$this->logger = $logger;
$this->remoteAddress = $remoteAddress;
$this->request = $request;
}
/**
* @param int|bool $websiteId
* @return array
*/
public function isValidationEnabled($websiteId = null)
{
return $this->scopeConfig->getValue(
'jicksonmagefix/settings/enable_validation',
\Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
}
/**
* @param int|bool $websiteId
*/
public function isAddressSaveValidationEnabled($websiteId = null)
{
return $this->scopeConfig->getValue(
'jicksonmagefix/settings/address_validation_enabled',
\Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
}
/**
* @param int|bool $websiteId
*/
public function getSuspiciousPatterns($websiteId = null)
{
return $this->scopeConfig->getValue(
'jicksonmagefix/settings/suspicious_patterns',
\Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
}
protected function isIpExempted($clientIp, $websiteId = null)
{
$useExemption = $this->scopeConfig->getValue(
'jicksonmagefix/settings/use_ip_exemption',
\Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
if ($useExemption) {
$exemptedIpsConfig = $this->scopeConfig->getValue(
'jicksonmagefix/settings/exempted_ips',
\Magento\Store\Model\ScopeInterface::SCOPE_WEBSITE,
$websiteId
);
$exemptedIps = array_filter(array_map('trim', explode(PHP_EOL, $exemptedIpsConfig)));
return in_array($clientIp, $exemptedIps);
} else {
return false;
}
}
public function validateAddressFields(array $addressData, $area)
{
$clientIp = $this->request->getClientIp();
// Check if the IP is exempted
if ($this->isIpExempted($clientIp)) {
return;
}
foreach ($addressData as $field => $value) {
// Example validation: Check for invalid characters or empty fields
if (is_array($value)) {
try {
if (empty($value)) {
continue; // Skip if the array is empty
}
// Convert all elements to strings before imploding
$value = implode(' ',$value);
} catch (\Exception $e) {
$this->logAccessDenied(json_encode($value), 'debugging');
$value = NULL;
}
}
if (isset($value)) {
// Check for usual strings
$fields = strtolower((string) $value);
// Check for suspicious strings
if ($this->containsSuspiciousString($value)) {
$this->logAccessDenied($fields, $area);
throw new LocalizedException(__("Field '$field' contains suspicious content."));
}
// Sansec solution that needs to be in app/bootstrap.php
/*$sanitizedInput = preg_replace("/[^A-Za-z]/", '', $value);
if (preg_match('/addafterfiltercallback/si', $sanitizedInput)) {
$this->logAccessDenied($sanitizedInput);
throw new LocalizedException(__($field . 'contains invalid value.'));
}*/
}
}
}
protected function containsSuspiciousString($value)
{
try {
// Retrieve the suspicious patterns and strings to find from system configuration
$patternsConfig = $this->getSuspiciousPatterns();
// Split patterns by newline and remove empty lines
$patterns = array_filter(array_map('trim', explode(PHP_EOL, $patternsConfig)));
// Convert the value to lowercase for case-insensitive checks
$fields = strtolower((string) $value);
} catch (\Exception $e) {
$this->logAccessDenied($e->getMessage(),'debugging');
$patterns = NULL;
$fields = NULL;
}
if (isset($patterns) && isset($fields)) {
// Iterate through the patterns and check if any matches the value
foreach ($patterns as $pattern) {
// If the pattern starts and ends with a slash, treat it as a regex
if (preg_match('/^\/.*\/[a-z]*$/i', $pattern)) {
if (@preg_match($pattern, $fields)) {
return true;
}
} else {
// Otherwise, treat it as a simple string search
if (strpos($fields, strtolower((string) $pattern)) !== false) {
return true;
}
}
}
}
return false;
}
protected function logAccessDenied($fields, $area)
{
$ip = $this->remoteAddress->getRemoteAddress();
$userAgent = $this->request->getServer('HTTP_USER_AGENT');
$timestamp = date('Y-m-d H:i:s');
// Prepare log message
$message = sprintf(
"[%s] %s : Request to save customer address is blocked. IP: %s, User-Agent: %s, Fields: %s",
$timestamp,
$area,
$ip,
$userAgent,
$fields
);
// Log the message to a custom file
$this->logger->critical($message);
}
}
7. Logging Suspicious Activities
The module includes custom logging functionality to record any suspicious attempts to manipulate data. These logs can be crucial for identifying patterns and improving security measures.
// app/code/Jickson/Mage24Fix/Logger/Handler.php
<?php
declare(strict_types=1);
namespace Jickson\Mage24Fix\Logger;
class Handler extends \Magento\Framework\Logger\Handler\Base
{
protected $loggerType = Logger::INFO;
protected $fileName = '/var/log/access_denied_logger.log';
}
// app/code/Jickson/Mage24Fix/Logger/Logger.php
<?php
declare(strict_types=1);
namespace Jickson\Mage24Fix\Logger;
class Logger extends \Monolog\Logger
{
}
Summary of the Module:
This module is designed to enhance security in Magento 2 by validating address fields during customer address saves in the frontend, as well as during quote and quote address saves. Additionally, it implements plugins to validate the checkout process REST APIs, thereby preventing unauthorized order creation through specific REST API endpoints.
Key Features:
- Address Validation: The module validates address fields when a customer saves their address in the frontend, as well as during the quote and quote address save events. This ensures that invalid data cannot be used in these fields, whether by legitimate users or potential attackers.
- REST API Security: The module specifically targets the
/rest/V1/guest-carts/
endpoint, which has been identified as a common target for attackers attempting to create fraudulent orders. By preventing unauthorized access and logging attempts, the module adds a layer of protection against such exploits. - Attack Logging: The module logs critical information about any blocked attempts, including the attacker’s IP address, user agent, and the data they attempted to enter. This information is stored in the
var/log/access_denied_logger.log
file, providing valuable insights for further security analysis.
Implementation Details:
- Observers:
customer_address_save_before
: Prevents registered customers from saving invalid address data.sales_quote_save_before
andsales_quote_address_save_before
: Ensures that invalid data cannot be used in quotes or during checkout, blocking attempts to create orders with invalid addresses.- Plugins:
beforeAssign
onMagento\Quote\Api\BillingAddressManagementInterface
andMagento\Quote\Model\ShippingAddressManagementInterface
: These plugins ensure that the validation logic is applied during the checkout process, further securing the order creation process.
Testing and Feedback: The module has been provided for you to create and test in your environment. It is essential to thoroughly test the module before deploying it to a production environment. You are encouraged to review the logic and make any necessary adjustments to fit your specific needs. Feedback on code improvements or potential bugs is welcome.
This module is a proactive step towards securing your Magento 2 store against common attack vectors related to address manipulation and unauthorized order creation.
As an emergency measure, it is recommended to check out this link from Sansec. They suggest adding the following code to app/bootstrap.php
to mitigate the threat:
if(preg_match('/addafterfiltercallback/si', preg_replace("/[^A-Za-z]/", '', urldecode(urldecode(file_get_contents("php://input")))))) {
header('HTTP/1.1 503 Service Temporarily Unavailable');
header('Status: 503 Service Temporarily Unavailable');
exit;
}
I suggest checking out Sansec’s guide to address HTTP requests. However, for additional protection against API-based attacks, my module complements Sansec’s fix by securing API endpoints effectively.
Patterns explanations
1. /{{.*}}/si:
This pattern looks for any content enclosed in double curly braces {{…}}, which is often used in templating languages to insert dynamic data. Purpose: To block attempts to inject or execute template code.
2. /\(\s*system\s*\)/si:
This checks for the word “system” inside parentheses, possibly with spaces around it. The system function is often used to execute system commands. Purpose: To prevent attempts to run system commands through function calls.
3. /\(\s*exec\s*\)/si:
Similar to the above, but this one targets the exec function, which is another method to execute system commands. Purpose: To block command execution attempts using the exec function.
4. /\$\{.*\}/si:
This pattern catches ${…}, which is often used in scripts to reference variables. Purpose: To prevent variable injection or manipulation in scripts.
5. /base64_/:
Looks for the substring base64_, which may be used in encoding/decoding operations that could obscure malicious content. Purpose: To block attempts at using Base64 encoding to hide malicious code.
6. /getTemp/:
Searches for getTemp, which might be related to temporary file or data handling, often used in scripts. Purpose: To detect and block script-related temporary data handling that might be suspicious.
7. /\.filter\(/:
Looks for .filter(, which is typically a method in programming to filter data or results. Purpose: To block attempts at manipulating or filtering data in unintended ways.
8. /\.addAfterFilterCallback/:
Matches .addAfterFilterCallback, which might be used in certain frameworks or code to add a callback function after a filter operation. Purpose: To prevent code injection through callback functions.
9. /cd\s+/si:
This checks for the cd command followed by a space, which is used to change directories in a command-line environment. Purpose: To prevent directory traversal or command execution attempts.
10. /pub;curl/:
Looks for the sequence pub;curl, which could be used to run the curl command, possibly to download and execute files. Purpose: To block attempts to use curl in command injections.
11. /\.php/:
Searches for .php, indicating the presence of PHP code or files. Purpose: To block attempts to include or execute PHP scripts.
12. gettemplate:
Searches for the word gettemplate, which might be used in code to retrieve or render templates. Purpose: To block potential template injection attacks.
13. base64_:
Another check for base64_, similar to the earlier pattern, targeting Base64 encoding/decoding. Purpose: To block encoded content that could hide malicious actions.
14. afterfiltercall:
Looks for afterfiltercall, possibly related to callback functions after a filter operation. Purpose: Similar to other filter-related checks, aimed at blocking script injection.
15. .filter(:
Another pattern looking for .filter(, targeting filter functions in programming. Purpose: To block data manipulation attempts.
16. @proton.me:
Searches for the email domain @proton.me, which might be used in phishing attempts. Purpose: To block suspicious email domains.
17. .php:
Another check for .php, similar to the earlier one, targeting PHP code or files. Purpose: To prevent PHP script injection.
18. this.getTemp:
Looks for this.getTemp, which could be related to temporary file handling in code. Purpose: To block suspicious temporary file operations.
19. {{var:
Checks for {{var, targeting template variables. Purpose: To block template injection attempts.
20. (system).Filter:
Looks for the combination of system and Filter, which might be used in certain code manipulations. Purpose: To block code or script manipulations involving system commands.
21. _decode:
Searches for _decode, which could be related to decoding operations, possibly to reveal hidden data. Purpose: To block encoded data that might hide malicious content.
22. cloud-cache:
Looks for cloud-cache, possibly related to cloud-based caching systems. Purpose: To block suspicious operations involving cloud caching.