data-src=../includes/paas-only.md
Rate limiting
In a carding attack, an attacker tries to determine which credit card numbers are valid, usually in batches of thousands. Attackers can use similar techniques to brute force missing details, such as the expiration date. Adobe Commerce merchants can be targeted by this attack type through their shops and integrations with third-party payment gateways.
As of Adobe Commerce 2.4.7, you can configure rate limiting for the payment information transmitted using REST and GraphQL. This added layer of protection allows merchants to prevent and decrease the volume of carding attacks that test many credit card numbers at once.
The rate limiting functionality affects the following entry points:
REST:
<base_url>/rest/V1/<store_code>/guest-carts/<cart_id>/payment-information<base_url>/rest/V1/<store_code>/guest-carts/<cart_id>/order<base_url>/rest/V1/<store_code>/carts/mine/payment-information<base_url>/rest/V1/<store_code>/carts/mine/order
GraphQL:
<base_url>/graphql
InstantPurchase module:
magento/module-instant-purchase
Setting up rate limiting has two discrete steps:
- Add a configuration that connects to the service where the request logs will be stored. By default, the connection is configured for a Redis server. Most merchants must configure a cloud or local installation of Redis to implement the rate limiting feature. However, merchants who have instances hosted on Amazon EC2 use an AWS ElastiCache instead of a cloud or local Redis instance.
Note: The data, including request time and identifier, is temporarily stored in Redis. Registered users are identified by their user ID. Non-registered users are identified by their external IP address.
- Configure Commerce to set restrictions on guest users and authenticated customers. This step is the same for all merchants.
Set the Redis service connection (most merchants)
The Configure Redis topic in the Commerce Configuration Guide describes basic installation and configuration information.
Commerce provides command-line options to configure the connection to the backpressure logger. Although you can configure the backpressure logger by editing the <Commerce-install-dir>/app/etc/env.php file, using the command line is the recommended method, especially for initial configurations. The command line provides validation, ensuring the configuration is syntactically correct.
By default, the connection is configured for a Redis server. Use the following bin/magento setup:config:set commands to configure the Redis service connection:
--backpressure-loggerredisredis.--backpressure-logger-id-prefix--backpressure-logger-redis-db3--backpressure-logger-redis-passwordauth command, which requires clients to authenticate to access the database. The password is configured directly in Redis' configuration file: /etc/redis/redis.conf--backpressure-logger-redis-persistent--backpressure-logger-redis-port6379--backpressure-logger-redis-server127.0.0.1--backpressure-logger-redis-timeout--backpressure-logger-redis-userThe following example creates a new connection to a Redis server with the following properties:
- Host: 195.34.23.5
- Port: 9345
- Password: s0M3StR0NgP@SsW0Rd
- User: SomeUser
$ bin/magento setup:config:set \
--backpressure-logger=redis \
--backpressure-logger-redis-server=195.34.23.5 \
--backpressure-logger-redis-port=9345 \
--backpressure-logger-redis-timeout=1 \
--backpressure-logger-redis-persistent=persistent \
--backpressure-logger-redis-db=3 \
--backpressure-logger-redis-password=s0M3StR0NgP@SsW0Rd\
--backpressure-logger-redis-user=SomeUser \
--backpressure-logger-id-prefix=some_pref
After the command is executed, the following configuration is added to the app/etc/env.php file.
[
//...
'backpressure' => [
'logger' => [
'type' => 'redis',
'options' => [
'server' => '195.34.23.5',
'port' => 9345,
'timeout' => 1,
'persistent' => 'persistent',
'db' => '3',
'password' => 's0meStr0ngPassw0rd',
'user' => 'SomeUser'
],
'id-prefix' => 'some_pref'
]
]
//...
];
Continue to Configure rate limiting
Use AWS ElastiCache with your EC2 instance
As of Commerce 2.4.3, instances hosted on Amazon EC2 can use an AWS ElastiCache in place of a local Redis instance.
data-variant=warning
data-slots=text
Configure a Redis cluster
After setting up a Redis cluster on AWS, configure the EC2 instance to use the ElastiCache.
-
Create an ElastiCache Cluster in the same region and VPC of the EC2 instance.
-
Verify the connection.
-
Open an SSH connection to your EC2 instance.
-
On the EC2 instance, install the Redis client:
sudo apt-get install redis -
Add an inbound rule to the EC2 security group: Type
- Custom TCP, port - 6379, Source - 0.0.0.0/0 -
Add an inbound rule to the ElastiCache Cluster security group: Type
- Custom TCP, port - 6379, Source - 0.0.0.0/0 -
Connect to the Redis CLI:
redis-cli -h <ElastiCache Primary Endpoint host> -p <ElastiCache Primary Endpoint port>
-
Configure Commerce to use the cluster
Run setup commands to specify the Redis endpoints. To configure Commerce for the backpressure logger connection:
bin/magento setup:config:set --backpressure-logger=redis --backpressure-logger-redis-server=<ElastiCache Primary Endpoint host> --backpressure-logger-redis-port=<ElastiCache Primary Endpoint port> --backpressure-logger-redis-db=3
Configure rate limiting
After the Redis server connection has been configured, you can run several bin/magento config:set commands that define how rate limiting is implemented for guest users and authenticated customers. Rate limiting is disabled by default.
You can also enable and configure rate limiting from the Admin by selecting Stores > Configuration > Sales > Sales > Rate Limiting.
Use the following commands to enable and configure rate limiting:
-
Enable (
1) or disable (0) rate limiting for placing orders:bin/magento config:set sales/backpressure/enabled 1 -
Set the request limit per guest (IP address).
bin/magento config:set sales/backpressure/guest_limit 50 -
Set the request limit for authenticated customers:
bin/magento config:set sales/backpressure/limit 10 -
Set the period of time (in seconds) for the request limit. Supported values
60,3600,86400seconds. This time period is multiplied by three to calculate the timeout period:bin/magento config:set sales/backpressure/period 60
The following scenario describes how rate limiting can be configured.
- Anonymous users are limited to 50 orders (
sales/backpressure/guest_limit=50) from a single IP address within one minute (sales/backpressure/period=60). If they exceed the order limit, then they will have to wait three times the specifiedperiodof time (180seconds) from their last request. - If an authorized customers attempts to place more than
10orders (sales/backpressure/limit=10) within theperiodof60seconds, then the user will not be able to place an order for a period of180seconds.
If you need to check a configuration, use the following CLI command:
Example:
bin/magento config:show | grep backpressure
Response:
sales/backpressure/limit - 10
sales/backpressure/guest_limit - 50
sales/backpressure/period - 60
sales/backpressure/enabled - 1
Log contents
If rate limiting has been enabled for the payment information endpoint and the GraphQL mutation via the UI/CLI, but the Redis service connection for store log requests has not been configured in the app/etc/env.php file, then the rate-limiting will not apply. The behavior will be the same if this option is disabled, but the application logs (<magento-root>/var/log/system.log) will contain the following message:
...
[2022-11-11T15:46:32.716679+00:00] main.ERROR: Backpressure sliding window not applied. Invalid request logger type: [] []
...
[2022-11-11T15:46:37.730863+00:00] main.ERROR: Backpressure sliding window not applied. Invalid request logger type: [] []
...
Example HTTP Responses
If rate limiting is applied to a REST request, then a response with HTTP status code 429 - Too Many Requests will be generated.
Example:
HTTP/1.1 429 Too Many Requests
...
Pragma: no-cache
Cache-Control: no-store
...
{"message":"Too Many Requests","trace":null}
If rate limiting is applied to a GraphQL request, then a response with HTTP status code 200 - Ok will be generated and all relevant information will be present in the response body.
Example:
HTTP/1.1 200 OK
...
Pragma: no-cache
Cache-Control: max-age=0, must-revalidate, no-cache, no-store
...
{
"errors":[
{
"message":"Too Many Requests",
"extensions":{"category":"graphql-too-many-requests"},
"locations":[
{"line":2,"column":3}
],
"path":["placeOrder"]
}
],
"data":{"placeOrder":null}
}