PHP / Magento Dev Blog

  • Publikacje
  • O autorze
  • Kontakt

REST API security – tokeny, ACL, rate limiting nginx, monitoring podejrzanych requestów

by Henryk Tews / wtorek, 19 marca 2024 / Opublikowano w Magento 2

Magento 2 REST API to potężne narzędzie – i potencjalny wektor ataku jeśli jest źle skonfigurowane. Domyślnie wiele endpointów jest dostępnych publicznie lub wymaga jedynie tokenu klienta który można wyprowadzić z frontendowej aplikacji. Pokazuję jak prawidłowo zabezpieczyć API: tokeny OAuth, rate limiting, ACL i monitoring podejrzanych requestów.

Typy tokenów w Magento 2 REST API

Typ tokenu Uprawnienia Jak uzyskać Ryzyko
Guest (brak tokenu) Tylko endpointy gość (koszyk, produkty) Brak autentykacji Scraping, DDoS
Customer token Dane zalogowanego klienta POST /V1/integration/customer/token Eksfiltracja danych klienta
Admin token Pełny dostęp do wszystkiego POST /V1/integration/admin/token Krytyczne – pełna kontrola sklepu
Integration token (OAuth) Konfigurowalny przez ACL Panel admin – Integrations Zależy od przyznanych uprawnień

Nigdy nie używaj Admin Token w aplikacjach zewnętrznych

<?php

// ŹLE - Admin Token w zewnętrznej integracji
$response = $client->post('/rest/V1/integration/admin/token', [
    'json' => ['username' => 'admin', 'password' => 'admin123']
]);
$adminToken = json_decode($response->getBody(), true);
// Ten token daje pełny dostęp do całego API - ogromne ryzyko!

// DOBRZE - Integration Token z ograniczonymi uprawnieniami przez ACL
// Skonfiguruj w: System -> Integrations -> Add New Integration
// Przyznaj tylko te zasoby których integracja rzeczywiście potrzebuje

Własne reguły ACL dla modułu

<!-- etc/acl.xml - definicja zasobów ACL -->
<?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::subscriptions" title="Newsletter Subscriptions" sortOrder="100">
                    <resource id="Vendor_Module::subscriptions_view"   title="View Subscriptions" sortOrder="10"/>
                    <resource id="Vendor_Module::subscriptions_create" title="Create Subscriptions" sortOrder="20"/>
                    <resource id="Vendor_Module::subscriptions_delete" title="Delete Subscriptions" sortOrder="30"/>
                </resource>
            </resource>
        </resources>
    </acl>
</config>
<!-- etc/webapi.xml - rejestracja endpointów z ACL -->
<?xml version="1.0"?>
<routes xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Webapi:etc/webapi.xsd">

    <!-- Endpoint dostępny dla każdego (gość) -->
    <route url="/V1/vendor/subscriptions" method="POST">
        <service class="Vendor\Module\Api\SubscriptionManagementInterface" method="subscribe"/>
        <resources>
            <resource ref="anonymous"/>
        </resources>
    </route>

    <!-- Endpoint tylko dla zalogowanego klienta -->
    <route url="/V1/vendor/subscriptions/me" method="GET">
        <service class="Vendor\Module\Api\SubscriptionManagementInterface" method="getMySubscriptions"/>
        <resources>
            <resource ref="self"/>  <!-- self = zalogowany klient -->
        </resources>
    </route>

    <!-- Endpoint tylko dla admina/integracji z odpowiednim uprawnieniem -->
    <route url="/V1/vendor/subscriptions/:id" method="DELETE">
        <service class="Vendor\Module\Api\SubscriptionManagementInterface" method="deleteById"/>
        <resources>
            <resource ref="Vendor_Module::subscriptions_delete"/>
        </resources>
    </route>

</routes>

Rate Limiting – ochrona przed nadużyciami

Magento 2.4.7+ ma wbudowany rate limiting dla endpointów autentykacji. Dla innych endpointów musisz dodać własny mechanizm lub użyć nginx/Varnish:

<?php

declare(strict_types=1);

namespace Vendor\Module\Plugin;

use Magento\Framework\App\RequestInterface;
use Magento\Framework\Cache\FrontendInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Webapi\Controller\Rest;

class RateLimitPlugin
{
    // Limity per IP
    private const LIMITS = [
        'guest'    => ['requests' => 60,  'window' => 60],   // 60 req/min
        'customer' => ['requests' => 300, 'window' => 60],   // 300 req/min
        'admin'    => ['requests' => 600, 'window' => 60],   // 600 req/min
    ];

    public function __construct(
        private FrontendInterface $cache,
        private RequestInterface $request
    ) {}

    public function beforeDispatch(Rest $subject, RequestInterface $request): array
    {
        $ip      = $this->getClientIp();
        $token   = $request->getHeader('Authorization') ?? '';
        $context = $token ? ($this->isAdminToken($token) ? 'admin' : 'customer') : 'guest';

        $limit  = self::LIMITS[$context];
        $key    = "rate_limit_{$context}_{$ip}";
        $cached = $this->cache->load($key);
        $count  = $cached ? (int) $cached : 0;

        if ($count >= $limit['requests']) {
            throw new LocalizedException(
                __('Too Many Requests. Please try again later.'),
                null,
                429
            );
        }

        // Inkrementuj licznik
        $this->cache->save((string) ($count + 1), $key, [], $limit['window']);

        return [$request];
    }

    private function getClientIp(): string
    {
        // Uwzględnij proxy/CDN headers
        $headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'REMOTE_ADDR'];

        foreach ($headers as $header) {
            $ip = $this->request->getServer($header);
            if ($ip) {
                return explode(',', $ip)[0]; // X-Forwarded-For może mieć listę
            }
        }

        return '0.0.0.0';
    }

    private function isAdminToken(string $authHeader): bool
    {
        // Prosta heurystyka - w praktyce możesz sprawdzić w bazie
        return str_starts_with($authHeader, 'Bearer ') && strlen($authHeader) > 40;
    }
}

Rate limiting na poziomie nginx – wydajniejsze rozwiązanie

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

# Definicja stref limitowania
limit_req_zone $binary_remote_addr zone=api_guest:10m    rate=60r/m;
limit_req_zone $binary_remote_addr zone=api_auth:10m     rate=300r/m;
limit_req_zone $binary_remote_addr zone=api_login:10m    rate=5r/m;

server {
    # ...

    # Endpoint logowania - bardzo restrykcyjny (ochrona przed brute force)
    location ~ ^/rest/V1/integration/(customer|admin)/token {
        limit_req zone=api_login burst=3 nodelay;
        limit_req_status 429;

        # Zwróć JSON zamiast domyślnego HTML Nginx przy 429
        error_page 429 /errors/429.json;

        fastcgi_pass fastcgi_backend;
        include fastcgi_params;
    }

    # Pozostałe endpointy API
    location /rest/ {
        # Rozróżnij gości od zalogowanych po nagłówku Authorization
        set $rate_zone api_guest;
        if ($http_authorization) {
            set $rate_zone api_auth;
        }

        limit_req zone=$rate_zone burst=20 nodelay;
        limit_req_status 429;

        fastcgi_pass fastcgi_backend;
        include fastcgi_params;
    }
}

Monitoring i alerty – wykrywanie nadużyć

<?php

declare(strict_types=1);

namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;

class ApiSecurityMonitor implements ObserverInterface
{
    // Endpointy które powinny być monitorowane
    private const SENSITIVE_ENDPOINTS = [
        '/V1/customers',
        '/V1/orders',
        '/V1/products',
    ];

    public function __construct(
        private LoggerInterface $logger,
        private \Magento\Framework\App\RequestInterface $request
    ) {}

    public function execute(Observer $observer): void
    {
        $uri    = $this->request->getRequestUri();
        $method = $this->request->getMethod();
        $ip     = $this->request->getServer('REMOTE_ADDR');

        // Loguj operacje zapisu na wrażliwych endpointach
        foreach (self::SENSITIVE_ENDPOINTS as $endpoint) {
            if (str_contains($uri, $endpoint) && in_array($method, ['POST', 'PUT', 'DELETE'], true)) {
                $this->logger->warning('Sensitive API operation', [
                    'method'     => $method,
                    'uri'        => $uri,
                    'ip'         => $ip,
                    'user_agent' => $this->request->getHeader('User-Agent'),
                ]);
            }
        }
    }
}

Ukrywanie informacji o wersji Magento

<?php

// Plugin usuwający nagłówek X-Magento-Cache-Id i inne wersjonoujące nagłówki
namespace Vendor\Module\Plugin;

class RemoveVersionHeadersPlugin
{
    public function afterSendResponse(
        \Magento\Framework\App\Response\Http $subject,
        \Magento\Framework\App\Response\Http $result
    ): \Magento\Framework\App\Response\Http {
        // Usuń nagłówki które zdradzają wersję/technologię
        $result->clearHeader('X-Powered-By');
        $result->clearHeader('Server');

        // Nie usuwaj X-Magento-Cache-Id jeśli używasz Varnish!
        // $result->clearHeader('X-Magento-Cache-Id');

        return $result;
    }
}

Checklist bezpieczeństwa REST API

  • Używaj Integration Tokens z minimalnym zestawem uprawnień ACL zamiast Admin Token
  • Włącz rate limiting na poziomie nginx dla endpointu logowania (ochrona przed brute force)
  • Ogranicz dostęp do /rest/ po IP dla integracji B2B które komunikują się ze znanych adresów
  • Monitoruj logi pod kątem 401/403 – duża liczba może oznaczać skanowanie endpointów
  • Wyłącz endpointy których nie używasz przez etc/webapi.xml override w własnym module
  • Ustaw HTTPS jako wymagane – nigdy nie przekazuj tokenów przez HTTP
  • Regularnie rotuj tokeny integracji – szczególnie jeśli są w zewnętrznych systemach

Podsumowanie

Bezpieczeństwo REST API w Magento 2 to kilka warstw: prawidłowy dobór i ograniczenie tokenów przez ACL, rate limiting który chroni przed brute force i nadużyciami, monitoring który daje wczesne ostrzeżenie i odpowiednia konfiguracja nginx jako pierwsza linia obrony. Admin Token w zewnętrznej integracji to niemal gwarancja problemów – Integration Token z minimalnym zestawem uprawnień to jedyna słuszna droga.

About Henryk Tews

Co możesz przeczytać następne

Magento 2.4.9 – oficjalne wsparcie PHP 8.5, co się zmieniło i jak migrować
Akeneo PIM – po co PIM, REST API, mapowanie atrybutów, import przez cron
Redis – trzy zastosowania, konfiguracja env.php, osobne instancje, monitoring
  • Publikacje
  • O autorze
  • Kontakt

© 2026 Created by

GÓRA
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 Zawsze aktywne
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.
  • Zarządzaj opcjami
  • Zarządzaj serwisami
  • Zarządzaj {vendor_count} dostawcami
  • Przeczytaj więcej o tych celach
Zobacz preferencje
  • {title}
  • {title}
  • {title}