Skip to content

Commit

Permalink
Merge pull request magento-commerce/devdocs#2332 from magento-devdocs…
Browse files Browse the repository at this point in the history
…/MC-40875-API-rate-limiting

API security - rate limiting
  • Loading branch information
jeff-matthews committed Jun 15, 2021
2 parents 009ffb2 + 02ef883 commit f951b55
Show file tree
Hide file tree
Showing 11 changed files with 219 additions and 63 deletions.
5 changes: 2 additions & 3 deletions src/_data/toc/graphql.yml
Expand Up @@ -15,9 +15,8 @@ pages:
- label: Protected mutations
url: /graphql/protected-mutations.html

- label: Query security
url: /graphql/query-security.html
exclude_versions: ["2.3"]
- label: GraphQL security configuration
url: /graphql/security-configuration.html

- label: GraphQL caching
url: /graphql/caching.html
Expand Down
3 changes: 3 additions & 0 deletions src/_data/toc/web-api.yml
Expand Up @@ -7,6 +7,9 @@ pages:
url: /get-started/rest_front.html
children:

- label: API security
url: /get-started/api-security.html

- label: Construct a request
url: /get-started/gs-web-api-request.html

Expand Down
51 changes: 0 additions & 51 deletions src/_includes/config/locate-session.md

This file was deleted.

56 changes: 55 additions & 1 deletion src/guides/v2.3/config-guide/sessions.md
Expand Up @@ -7,4 +7,58 @@ functional_areas:
- Setup
---

{% include config/locate-session.md %}
## Locate your session storage {#session-where}

This topic discusses how to locate where your session files are stored. The system uses the following logic to store session files:

* If you configured memcached, sessions are stored in RAM; for more information, see [Use memcached for session storage]({{ page.baseurl }}/config-guide/memcache/memcache.html).
* If you configured Redis, sessions are stored on the Redis server; for more information, see [Use Redis for page caching or session storage]({{ page.baseurl }}/config-guide/redis/config-redis.html).
* If you're using the default file-based session storage, we store sessions in the following locations in the order shown:

1. Directory defined in [`env.php`](#session-where-env)
1. Directory defined in [`php.ini`](#session-where-phpini)
1. `<magento_root>/var/session` directory

### `env.php` example {#session-where-env}

A sample snippet from `<magento_root>/app/etc/env.php` follows:

```php
'session' =>
array (
'save' => 'files',
'save_path' => '/var/www/session',
),
```

The preceding example stores session files in `/var/www/session`

### `php.ini` example {#session-where-phpini}

As a user with `root` privileges, open your `php.ini` file and search for the value of `session.save_path`. This identifies where sessions are stored.

## Manage session size

See the [User Guide](https://docs.magento.com/user-guide/stores/security-session-management.html).

## Garbage collection configuration {#session-gc}

To clean up expired sessions, the system calls the `gc` (_garbage collection_) handler randomly according to a probability that is calculated by the `gc_probability / gc_divisor` directive. For example, if you set these directives to `1/100` respectively, it means a probability of `1%` (_probability of one call of garbage collection per 100 requests_).

The garbage collection handler uses the `gc_maxlifetime` directive—the number of seconds after which the sessions will be seen as _garbage_ and potentially cleaned up.

On some operating systems (Debian/Ubuntu), the default `session.gc_probability` directive is `0`, which prevents the garbage collection handler from running.

You can overwrite the `session.gc_` directives from the `php.ini` file in the `<magento_root>/app/etc/env.php` file:

```php
'session' =>
array (
'save' => 'db',
'gc_probability' => 1,
'gc_divisor' => 1000,
'gc_maxlifetime' => 1440
),
```

The configuration varies, depending on the traffic and specific needs of the merchant's website.
114 changes: 114 additions & 0 deletions src/guides/v2.3/get-started/api-security.md
@@ -0,0 +1,114 @@
---
group: web-api
title: API security
functional_areas:
- Integration
---

This topic describes best practices for [API security](https://owasp.org/www-project-api-security/).

## Rate limiting

Imposing restrictions on the size and number of resources that a user can request through an API can help mitigate denial-of-service (DoS) vulnerabilities. By default, the following built-in API rate limiting is available:

- REST requests containing inputs representing a list of entities are limited to a default maximum of 20 entities
- REST and GraphQL queries that allow paginated results are limited to a default maximum of 300 items per page

{:.bs-callout-info}
In addition, the Admin provides a configuration setting for limiting session sizes for Admin users and storefront visitors.

You can customize the default limits programmatically using [class constructor arguments]({{ page.baseurl }}/extension-dev-guide/build/di-xml-file.html).

### Maximum parameter inputs

The `EntityArrayValidator` class constructor limits the number of objects that can be given to inputs that represent arrays of objects. For example, the `PUT /V1/guest-carts/{cartId}/collect-totals` endpoint contains the input parameter `additionalData->extension_attributes->gift_messages`, which represents a list of gift message information objects.

There are four possible input arrays:

- `additional_data`
- `agreement_ids`
- `gift_messages`
- `custom_attributes`

```json
{
"paymentMethod": {
"po_number": "string",
"method": "string",
"additional_data": [
"string"
],
"extension_attributes": {
"agreement_ids": [
"string"
]
}
},
"shippingCarrierCode": "string",
"shippingMethodCode": "string",
"additionalData": {
"extension_attributes": {
"gift_messages": [
{
"gift_message_id": 0,
"customer_id": 0,
"sender": "string",
"recipient": "string",
"message": "string",
"extension_attributes": {
"entity_id": "string",
"entity_type": "string",
"wrapping_id": 0,
"wrapping_allow_gift_receipt": true,
"wrapping_add_printed_card": true
}
}
]
},
"custom_attributes": [
{
"attribute_code": "string",
"value": "string"
}
]
}
}
```

By default, any one of these arrays can include up to 20 items, but you can override the default by specifying a new value for the `complexArrayItemLimit` argument in the `EntityArrayValidator` class constructor inside your module's `di.xml` file.

The `EntityArrayValidator` class constructor restricts the number of inputs allowed to this parameter. By default, the maximum value is `20`. You can change the default in your module's `di.xml` file. The following example changes the limit to `30`:

```xml
<type name="Magento\Framework\Webapi\Validator\EntityArrayValidator">
<arguments>
<argument name="complexArrayItemLimit" xsi:type="number">30</argument>
</arguments>
</type>
```

### Maximum page size

The `SearchCriteriaValidator` class constructor limits the maximum page size, which controls the pagination of various web API responses. By default, the maximum value is `300`. You can change the default in your module's `di.xml` file. The following example changes the limit to `200`:

```xml
<type name="Magento\Framework\Webapi\Validator\SearchCriteriaValidator">
<arguments>
<argument name="maximumPageSize" xsi:type="number">200</argument>
</arguments>
</type>
```

[GraphQL security configuration]({{page.baseurl}}/graphql/security-configuration.html) describes how to set the maximum page size in GraphQL.

### Default page size

The `ServiceInputProcessor` class constructor defines the default page size, which controls the pagination of various web API responses. You can change the default value of `20` in your custom module's `di.xml` file. The following example changes the default page size to `25`:

```xml
<type name="Magento\Framework\Webapi\ServiceInputProcessor">
<arguments>
<argument name="defaultPageSize" xsi:type="number">25</argument>
</arguments>
</type>
```
18 changes: 18 additions & 0 deletions src/guides/v2.3/graphql/security-configuration.md
@@ -0,0 +1,18 @@
---
group: graphql
title: GraphQL security configuration
---

The Framework `app/etc/di.xml` file uses the `maxPageSize` argument to restrict the maximum page size in queries to 300. To override this default value, create a custom module and provide a new value in the module's [di.xml]({{page.baseurl}}/extension-dev-guide/build/di-xml-file.html) file.

The following example changes the limit to `100`:

```xml
<type name="Magento\Framework\GraphQl\Query\Resolver\Argument\Validator\SearchCriteriaValidator">
<arguments>
<argument name="maxPageSize" xsi:type="number">100</argument>
</arguments>
</type>
```

[API security]({{page.baseurl}}/get-started/api-security.html) describes additional arguments that are applicable to web APIs in general.
1 change: 1 addition & 0 deletions src/guides/v2.4/get-started/api-security.md
2 changes: 1 addition & 1 deletion src/guides/v2.4/graphql/queries/categories.md
Expand Up @@ -16,7 +16,7 @@ The `categories` query supports the following types of filters. You can specify
The query returns a `CategoryResult` object, which contains pagination information and an array of `CategoryTree` items. The top level of the `CategoryTree` object provides details about the queried category. This object includes the `children` attribute, which contains an array of its immediate subcategories.

{:.bs-callout-info}
You cannot return the entire category tree if the total number of nodes in the request exceeds the value specified in the `queryDepth` attribute defined in the GraphQL `di.xml` file. By default, this value is 20. [Query security]({{page.baseurl}}/graphql/query-security.html) further describes query depths.
You cannot return the entire category tree if the total number of nodes in the request exceeds the value specified in the `queryDepth` attribute defined in the GraphQL `di.xml` file. By default, this value is 20. [Query security]({{page.baseurl}}/graphql/security-configuration.html) further describes query depths.

Use the `breadcrumbs` attribute to return information about the parent categories of the queried category.

Expand Down
2 changes: 1 addition & 1 deletion src/guides/v2.4/graphql/queries/category-list.md
Expand Up @@ -21,7 +21,7 @@ If you do not provide any filter input, the query returns the root category.
The query returns a `CategoryTree` object. The top level of the `CategoryTree` object provides details about the queried category. This object includes the `children` attribute, which contains an array of its immediate subcategories.

{:.bs-callout-info}
You cannot return the entire category tree if the total number of nodes in the request exceeds the value specified in the `queryDepth` attribute defined in the GraphQL `di.xml` file. By default, this value is 20. [Query security]({{page.baseurl}}/graphql/query-security.html) further describes query depths.
You cannot return the entire category tree if the total number of nodes in the request exceeds the value specified in the `queryDepth` attribute defined in the GraphQL `di.xml` file. By default, this value is 20. [Query security]({{page.baseurl}}/graphql/security-configuration.html) further describes query depths.

Use the `breadcrumbs` attribute to return information about the parent categories of the queried category.

Expand Down
2 changes: 1 addition & 1 deletion src/guides/v2.4/graphql/queries/category.md
Expand Up @@ -9,7 +9,7 @@ The `category` query has been deprecated. Use the [categories]({{page.baseurl}}/
The `category` query allows you to search for a single category definition or the entire category tree.

{:.bs-callout-info}
You cannot return the entire category tree if the total number of nodes in the request exceeds the value specified in the `queryDepth` attribute defined in the GraphQL `di.xml` file. By default, this value is 20. [Query security]({{page.baseurl}}/graphql/query-security.html) further describes query depths.
You cannot return the entire category tree if the total number of nodes in the request exceeds the value specified in the `queryDepth` attribute defined in the GraphQL `di.xml` file. By default, this value is 20. [Query security]({{page.baseurl}}/graphql/security-configuration.html) further describes query depths.

## Syntax

Expand Down
@@ -1,18 +1,36 @@
---
group: graphql
title: Query security
title: GraphQL security configuration
---

The Framework and `GraphQl` module `di.xml` files define several security-related configuration values that you should review to ensure they align with types of mutations and queries that you run.

To override these default values, create a custom module and provide a new value in the appropriate [di.xml]({{page.baseurl}}/extension-dev-guide/build/di-xml-file.html) file.

## Framework configuration

In GraphQL, the `SearchCriteriaValidator` class constructor limits the maximum page size in queries to `300` by default as well. You can change the default in the `di.xml` file. The following example changes the limit to `100`:

```xml
<type name="Magento\Framework\GraphQl\Query\Resolver\Argument\Validator\SearchCriteriaValidator">
<arguments>
<argument name="maxPageSize" xsi:type="number">100</argument>
</arguments>
</type>
```

[API security]({{page.baseurl}}/get-started/api-security.html) describes additional arguments that are applicable to web APIs in general.

## GraphQl module configuration

The `GraphQl/etc/di.xml` file contains two arguments that can be overridden to enhance security and prevent performance bottlenecks:

Attribute | Default value | Description
--- | --- | ---
`queryComplexity` | 300 | Defines the maximum number of fields, objects, and fragments that a query can contain.
`queryDepth` | 20 | Defines the maximum depth of nodes that query can return.

To override these default values, create a custom module and provide a new value in the module's [di.xml]({{page.baseurl}}/extension-dev-guide/build/di-xml-file.html).

## Query complexity
### Query complexity

A complex GraphQL query, such as the [`cart`]({{page.baseurl}}/graphql/queries/cart.html) or [`products`]({{page.baseurl}}/graphql/queries/products.html) query, can potentially generate a heavy workload on the server. Complex queries can potentially be used to create distributed denial of service (DDoS) attacks by overloading the server with specious requests.

Expand Down Expand Up @@ -60,7 +78,7 @@ Creating the `name1` alias did not cause the system to double count the entry.

If the count does not exceed the threshold set by the `queryComplexity` attribute, Magento validates and processes the query.

## Query depth
### Query depth

The `queryDepth` attribute specifies the maximum depth a query can return. This can be an issue for queries that return objects that show a hierarchy, such as [`CategoryTree`]({{page.baseurl}}/graphql/queries/categories.html), or queries that return detailed data on complex [products]({{page.baseurl}}/graphql/queries/products.html). The default value of 20 allows for deep hierarchies and products, but you might want to reduce this number if you know that legitimate queries will never reach that depth.

Expand Down

0 comments on commit f951b55

Please sign in to comment.