PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

REST API security – tokens, ACL, nginx rate limiting, monitoring suspicious requests

by Henryk Tews / Tuesday, 19 March 2024 / Published in Magento 2

Magento 2 REST API is powerful but exposed to abuse if not properly secured. Default token authentication, over-permissive ACL resources, no rate limiting, and logging gaps are the most common issues I see on production installations. I show a layered security approach: proper token management, granular ACL, nginx rate limiting, and monitoring for suspicious patterns.

Authentication tokens – the basics done right

# Generate customer token
curl -X POST https://shop.example.com/rest/V1/integration/customer/token \
  -H 'Content-Type: application/json' \
  -d '{"username": "customer@example.com", "password": "password123"}'

# Generate admin token (NEVER use in frontend code!)
curl -X POST https://shop.example.com/rest/V1/integration/admin/token \
  -H 'Content-Type: application/json' \
  -d '{"username": "admin", "password": "admin_password"}'

# Use the token
curl -X GET https://shop.example.com/rest/V1/customers/me \
  -H 'Authorization: Bearer eyJraWQiOiIxIiwiYWxnIjoiSFMyNTYifQ...'
<?php

// Programmatic token generation with expiry management
class ApiTokenService
{
    public function __construct(
        private \Magento\Integration\Model\Oauth\TokenFactory $tokenFactory,
        private \Magento\Integration\Model\ResourceModel\Oauth\Token $tokenResource,
        private \Magento\Customer\Api\CustomerRepositoryInterface $customerRepository
    ) {}

    public function generateCustomerToken(int $customerId): string
    {
        $customer = $this->customerRepository->getById($customerId);
        $token    = $this->tokenFactory->create();

        $token->createCustomerToken($customerId);
        $this->tokenResource->save($token);

        return $token->getToken();
    }

    public function revokeCustomerTokens(int $customerId): void
    {
        // Revoke all active tokens for a customer (e.g. on password change)
        $this->tokenResource->revokeCustomerTokens([$customerId]);
    }
}

Granular ACL – least privilege principle

<!-- etc/acl.xml - define fine-grained permissions -->
<?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="Vendor_Module::root" title="Vendor Module" sortOrder="999">
                    <resource id="Vendor_Module::read"
                              title="Read products" sortOrder="10"/>
                    <resource id="Vendor_Module::write"
                              title="Create/update products" sortOrder="20"/>
                    <resource id="Vendor_Module::delete"
                              title="Delete products" sortOrder="30"/>
                </resource>
            </resource>
        </resources>
    </acl>
</config>
<!-- etc/webapi.xml - assign ACL to endpoints -->
<route url="/V1/vendor-module/products" method="GET">
    <service class="Vendor\Module\Api\ProductRepositoryInterface" method="getList"/>
    <resources>
        <resource ref="Vendor_Module::read"/>   <!-- requires read permission only -->
    </resources>
</route>

<route url="/V1/vendor-module/products" method="POST">
    <service class="Vendor\Module\Api\ProductRepositoryInterface" method="save"/>
    <resources>
        <resource ref="Vendor_Module::write"/>  <!-- requires write permission -->
    </resources>
</route>

<route url="/V1/vendor-module/products/:id" method="DELETE">
    <service class="Vendor\Module\Api\ProductRepositoryInterface" method="deleteById"/>
    <resources>
        <resource ref="Vendor_Module::delete"/> <!-- requires delete permission -->
    </resources>
</route>

nginx rate limiting – protect the token endpoint

# /etc/nginx/conf.d/magento.conf

# Rate limit zone: 5 requests per second per IP for auth endpoints
limit_req_zone $binary_remote_addr zone=auth:10m rate=5r/s;

# Rate limit zone: 100 req/s per IP for API endpoints
limit_req_zone $binary_remote_addr zone=api:10m rate=100r/s;

server {
    # ...

    # Strict rate limit on token endpoints - brute force protection
    location ~ ^/rest/V1/integration/(admin|customer)/token {
        limit_req zone=auth burst=10 nodelay;
        limit_req_status 429;

        # Optional: log brute force attempts
        access_log /var/log/nginx/auth_access.log combined;

        # ... proxy to PHP-FPM
        fastcgi_pass fastcgi_backend;
        include fastcgi_params;
    }

    # General API rate limiting
    location /rest/ {
        limit_req zone=api burst=200 nodelay;
        limit_req_status 429;

        # Add rate limit headers
        add_header X-RateLimit-Limit 100;
        add_header Retry-After 1;

        fastcgi_pass fastcgi_backend;
        include fastcgi_params;
    }
}

Monitoring suspicious patterns

# Detect brute force on token endpoint
awk '/POST.*\/rest\/V1\/integration.*\/token/ {print $1}' /var/log/nginx/access.log | \
    sort | uniq -c | sort -rn | head -20
# IP with 100+ attempts in last hour = suspected brute force

# Find IPs hitting the API without auth (401 responses)
awk '$9 == "401" {print $1}' /var/log/nginx/access.log | \
    sort | uniq -c | sort -rn | head -20

# Monitor for unusually large responses (data exfiltration indicator)
awk '$10 > 1000000 {print $0}' /var/log/nginx/access.log | \
    grep "rest/V1" | tail -20

# Automated alert: block IPs with 100+ 401s in 5 minutes
# (can integrate with fail2ban or cloudflare)
<?php

// Magento plugin: log all API authentication failures
namespace Vendor\Security\Plugin;

class ApiAuthLogger
{
    public function __construct(private \Psr\Log\LoggerInterface $logger) {}

    public function afterAuthenticate(
        \Magento\Webapi\Model\Authorization\TokenUserContext $subject,
        $result
    ) {
        if ($subject->getUserId() === null) {
            $this->logger->warning('API auth failure', [
                'ip'  => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                'uri' => $_SERVER['REQUEST_URI'] ?? '',
                'ua'  => $_SERVER['HTTP_USER_AGENT'] ?? '',
            ]);
        }
        return $result;
    }
}

Summary

API security in Magento 2 is layered: token management at the application level, granular ACL on each endpoint, nginx rate limiting at the web server level, and monitoring to detect patterns before they become incidents. The most impactful single change is nginx rate limiting on the token endpoint – it stops brute force credential attacks that bypass application-level protections. Combine it with fail2ban to automatically block offending IPs.

About Henryk Tews

What you can read next

Strategy pattern in PHP – and how Magento 2 uses it in pricing
Xdebug – configuration, PHPStorm, debugging Magento plugins

© 2026 Created by

TOP
Zarządzaj zgodą
Aby zapewnić jak najlepsze wrażenia, korzystamy z technologii, takich jak pliki cookie, do przechowywania i/lub uzyskiwania dostępu do informacji o urządzeniu. Zgoda na te technologie pozwoli nam przetwarzać dane, takie jak zachowanie podczas przeglądania lub unikalne identyfikatory na tej stronie. Brak wyrażenia zgody lub wycofanie zgody może niekorzystnie wpłynąć na niektóre cechy i funkcje.
Funkcjonalne Always active
Przechowywanie lub dostęp do danych technicznych jest ściśle konieczny do uzasadnionego celu umożliwienia korzystania z konkretnej usługi wyraźnie żądanej przez subskrybenta lub użytkownika, lub wyłącznie w celu przeprowadzenia transmisji komunikatu przez sieć łączności elektronicznej.
Preferencje
Przechowywanie lub dostęp techniczny jest niezbędny do uzasadnionego celu przechowywania preferencji, o które nie prosi subskrybent lub użytkownik.
Statystyka
Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do celów statystycznych. Przechowywanie techniczne lub dostęp, który jest używany wyłącznie do anonimowych celów statystycznych. Bez wezwania do sądu, dobrowolnego podporządkowania się dostawcy usług internetowych lub dodatkowych zapisów od strony trzeciej, informacje przechowywane lub pobierane wyłącznie w tym celu zwykle nie mogą być wykorzystywane do identyfikacji użytkownika.
Marketing
Przechowywanie lub dostęp techniczny jest wymagany do tworzenia profili użytkowników w celu wysyłania reklam lub śledzenia użytkownika na stronie internetowej lub na kilku stronach internetowych w podobnych celach marketingowych.
  • Manage options
  • Manage services
  • Manage {vendor_count} vendors
  • Read more about these purposes
Zobacz preferencje
  • {title}
  • {title}
  • {title}