# Безпека REST API у Symfony 2026: OAuth2, Rate Limiting та питання на співбесідах > Комплексний посібник із захисту REST API у Symfony за допомогою OAuth2 token introspection, rate limiting та валідації JWT. Огляд функцій безпеки Symfony 7.3 та питання для співбесід. - Published: 2026-09-14 - Updated: 2026-09-14 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Безпека REST API у Symfony вимагає багаторівневого підходу, що поєднує автентифікацію, авторизацію та контроль трафіку. Symfony 7.3 впровадив нативну підтримку OAuth2 token introspection згідно з RFC 7662, тоді як компонент RateLimiter забезпечує вбудований захист від зловживань. > **Ключові рівні безпеки для API Symfony** > > Продакшн-готовий API Symfony потребує трьох рівнів захисту: автентифікації (хто викликає), авторизації (до чого має доступ) та rate limiting (як часто). Відсутність будь-якого рівня наражає застосунок на credential stuffing, витік даних або атаку denial of service. ## OAuth2 Token Introspection у Symfony 7.3 Symfony 7.3 додає вбудовану підтримку [OAuth2 Token Introspection (RFC 7662)](https://datatracker.ietf.org/doc/html/rfc7662). Ця функція валідує токени доступу шляхом прямого запиту до сервера авторизації, усуваючи необхідність локального декодування токенів. Це важливо, коли формат токена непрозорий або контролюється стороннім провайдером ідентифікації. Конфігурація `AccessTokenHandler` приймає endpoint introspection: ```yaml # config/packages/security.yaml security: firewalls: api: pattern: ^/api stateless: true access_token: token_handler: introspection: client_id: '%env(OAUTH_CLIENT_ID)%' client_secret: '%env(OAUTH_CLIENT_SECRET)%' introspection_url: '%env(OAUTH_INTROSPECTION_URL)%' ``` Відповідь introspection містить `active`, `scope`, `client_id` та опціонально `username`. Symfony автоматично відображає ці значення на атрибути безпеки. ```php // src/Controller/Api/ProfileController.php namespace App\Controller\Api; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\Routing\Attribute\Route; use Symfony\Component\Security\Http\Attribute\IsGranted; #[Route('/api/profile', methods: ['GET'])] #[IsGranted('ROLE_USER')] class ProfileController extends AbstractController { public function __invoke(): JsonResponse { // Токен уже валідований через introspection $user = $this->getUser(); return $this->json([ 'id' => $user->getId(), 'email' => $user->getEmail(), 'scopes' => $user->getRoles(), ]); } } ``` Це переносить навантаження верифікації підпису JWT з застосунку на сервер авторизації. Сервер авторизації займається ротацією ключів, відкликанням токенів та валідацією областей. ## Імплементація Rate Limiting з компонентом RateLimiter [Компонент RateLimiter](https://symfony.com/doc/current/rate_limiter.html) захищає endpoint'и від зловживань, використовуючи алгоритми token bucket або sliding window. Конфігурація відбувається у `framework.yaml`: ```yaml # config/packages/framework.yaml framework: rate_limiter: # Загальний ліміт API: 100 запитів на хвилину api_limiter: policy: sliding_window limit: 100 interval: '1 minute' # Суворіший ліміт для endpoint'ів автентифікації login_limiter: policy: token_bucket limit: 5 rate: { interval: '1 minute', amount: 5 } ``` Rate limiting можна застосувати до контролерів за допомогою event subscriber: ```php // src/EventSubscriber/RateLimitSubscriber.php namespace App\EventSubscriber; use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\RateLimiter\RateLimiterFactory; final class RateLimitSubscriber implements EventSubscriberInterface { public function __construct( private readonly RateLimiterFactory $apiLimiter, ) {} public static function getSubscribedEvents(): array { return [KernelEvents::REQUEST => ['onRequest', 10]]; } public function onRequest(RequestEvent $event): void { if (!$event->isMainRequest()) { return; } $request = $event->getRequest(); // Застосувати лише до API маршрутів if (!str_starts_with($request->getPathInfo(), '/api')) { return; } // Використати IP клієнта або автентифікованого користувача як ключ лімітера $key = $request->getClientIp(); $limiter = $this->apiLimiter->create($key); if (!$limiter->consume()->isAccepted()) { throw new TooManyRequestsHttpException(); } } } ``` Для автентифікованих API варто замінити ключ на основі IP ідентифікатором користувача, щоб запобігти впливу одного зловмисного користувача на легітимний трафік з тієї ж мережі. ## Валідація JWT без зовнішніх залежностей Коли сервер авторизації видає JWT з відомим публічним ключем, Symfony може валідувати токени локально, використовуючи `OidcUserInfoTokenHandler`: ```yaml # config/packages/security.yaml security: firewalls: api: pattern: ^/api stateless: true access_token: token_handler: oidc_user_info: base_uri: '%env(OIDC_ISSUER)%' claim: email ``` Для власних JWT без OIDC провайдера можна використати `lexik/jwt-authentication-bundle`: ```php // src/Security/JwtTokenAuthenticator.php namespace App\Security; use Lexik\Bundle\JWTAuthenticationBundle\Services\JWTTokenManagerInterface; use Symfony\Component\Security\Http\AccessToken\AccessTokenHandlerInterface; use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge; final class JwtTokenAuthenticator implements AccessTokenHandlerInterface { public function __construct( private readonly JWTTokenManagerInterface $jwtManager, ) {} public function getUserBadgeFrom(string $accessToken): UserBadge { // Автоматично декодує та валідує підпис $payload = $this->jwtManager->parse($accessToken); return new UserBadge($payload['sub']); } } ``` Bundle JWT обробляє верифікацію підпису RS256/ES256, перевірку терміну дії та валідацію видавця. Публічний ключ слід зберігати в `config/jwt/public.pem` і посилатися на нього в `lexik_jwt_authentication.yaml`. ## Захист endpoint'ів API за допомогою Voter'ів Symfony Voter'и забезпечують детальну авторизацію, що виходить за межі простих перевірок ролей. Типовий патерн перевіряє володіння ресурсом: ```php // src/Security/Voter/ArticleVoter.php namespace App\Security\Voter; use App\Entity\Article; use App\Entity\User; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Authorization\Voter\Voter; final class ArticleVoter extends Voter { public const EDIT = 'ARTICLE_EDIT'; public const DELETE = 'ARTICLE_DELETE'; protected function supports(string $attribute, mixed $subject): bool { return in_array($attribute, [self::EDIT, self::DELETE], true) && $subject instanceof Article; } protected function voteOnAttribute( string $attribute, mixed $subject, TokenInterface $token ): bool { $user = $token->getUser(); if (!$user instanceof User) { return false; } /** @var Article $article */ $article = $subject; // Адміністратори можуть все if (in_array('ROLE_ADMIN', $user->getRoles(), true)) { return true; } // Автори можуть редагувати/видаляти власні статті return $article->getAuthor() === $user; } } ``` Використання voter'а в контролерах: ```php #[Route('/api/articles/{id}', methods: ['PUT'])] public function update(Article $article, Request $request): JsonResponse { $this->denyAccessUnlessGranted(ArticleVoter::EDIT, $article); // Логіка оновлення } ``` Voter'и централізують логіку авторизації та роблять її тестованою. Вони також часто з'являються в [питаннях на співбесідах щодо Symfony](/technologies/symfony/interview-questions/events-subscribers) про компонент безпеки. ## Типові вразливості API та способи їх усунення [OWASP API Security Top 10](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) визначає поширені вразливості в API. Symfony забезпечує вбудований захист від багатьох з них: | Вразливість | Захист у Symfony | |-------------|------------------| | Broken Object Level Authorization | Voter'и з перевіркою володіння | | Broken Authentication | Throttling логіну, безпечне хешування паролів | | Excessive Data Exposure | Групи серіалізації DTO | | Lack of Rate Limiting | Компонент RateLimiter | | Mass Assignment | Валідація форм, маппінг DTO | | Security Misconfiguration | Symfony security checker, змінні середовища | Для захисту від mass assignment ніколи не слід гідратувати сутності безпосередньо з даних запиту: ```php // src/Dto/CreateArticleDto.php namespace App\Dto; use Symfony\Component\Validator\Constraints as Assert; final class CreateArticleDto { public function __construct( #[Assert\NotBlank] #[Assert\Length(max: 255)] public readonly string $title, #[Assert\NotBlank] public readonly string $content, // Навмисно пропущено: author, createdAt, status // Ці значення встановлюються застосунком, не клієнтом ) {} } ``` Підхід з білим списком DTO запобігає встановленню клієнтами полів, які вони не повинні контролювати, таких як `isAdmin` чи `createdAt`. ## Конфігурація CORS для споживачів API Заголовки Cross-Origin Resource Sharing контролюють, які домени можуть викликати API з браузерів. Bundle `nelmio/cors-bundle` забезпечує декларативну конфігурацію: ```yaml # config/packages/nelmio_cors.yaml nelmio_cors: defaults: allow_origin: ['%env(CORS_ALLOW_ORIGIN)%'] allow_methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'] allow_headers: ['Content-Type', 'Authorization'] max_age: 3600 paths: '^/api/': origin_regex: true allow_origin: ['^https://.*\.example\.com$'] allow_credentials: true ``` Слід уникати `allow_origin: ['*']` у поєднанні з `allow_credentials: true`. Ця комбінація наражає API на крадіжку облікових даних через зловмисні сайти. Краще використовувати явні білі списки доменів або regex-патерни. ## Технічні питання на співбесідах щодо безпеки API Symfony Ці питання часто з'являються на співбесідах для старших розробників Symfony. Відповіді відображають можливості Symfony 7.3. **П: Як Symfony обробляє валідацію токенів доступу OAuth2?** Symfony 7.3 підтримує три підходи: локальну валідацію JWT з верифікацією підпису, виклики endpoint'у OIDC user info та [RFC 7662 token introspection](https://symfony.com/blog/new-in-symfony-7-3-security-improvements). Introspection підходить для непрозорих токенів або сценаріїв, коли застосунок не контролює сервер авторизації. `AccessTokenHandler` абстрагує всі три за уніфікованим інтерфейсом. **П: Яка різниця між firewall'ами та access control у Symfony?** Firewall'и визначають механізми автентифікації для URL-патернів. Правила access control визначають вимоги авторизації після успішної автентифікації. Firewall може вимагати дійсний JWT, тоді як access control вимагає `ROLE_ADMIN` для `/api/admin/*`. Вони працюють послідовно: firewall автентифікує, потім access control авторизує. **П: Як запобігти brute force атакам на endpoint логіну?** Вбудований login throttling у Symfony обмежує невдалі спроби за ім'ям користувача та IP. Конфігурація `login_throttling` у firewall: ```yaml security: firewalls: main: login_throttling: max_attempts: 5 interval: '15 minutes' ``` Для API автентифікації слід поєднати з компонентом RateLimiter на endpoint'і токенів. Лічильники спроб варто зберігати в Redis для multi-instance розгортань. **П: Поясніть Symfony Voter'и та коли їх використовувати замість простих перевірок ролей.** Voter'и реалізують складну логіку авторизації, що залежить від ресурсу, а не лише від ролі користувача. Voter'и слід використовувати коли: користувач повинен бути власником ресурсу, ресурс має машину станів (чернетка vs опублікований), або авторизація залежить від бізнес-правил (рівень підписки). Перевірки ролей достатні для статичних дозволів на кшталт "лише адміністратори мають доступ до налаштувань." Більше питань про безпеку Symfony можна знайти в [посібнику підготовки до співбесіди Symfony](/blog/symfony/symfony-interview-questions). ## Побудова безпечного API Symfony: Ключові висновки - Налаштуйте OAuth2 token introspection для зовнішніх провайдерів ідентифікації, коли формат токена непрозорий - Застосовуйте rate limiting на рівні інфраструктури (reverse proxy) та застосунку (компонент RateLimiter) для глибокого захисту - Використовуйте Voter'и для авторизації на рівні ресурсів, резервуючи перевірки ролей для статичних дозволів - Мапіть дані запитів на DTO з явними властивостями для запобігання mass assignment - Зберігайте секрети в змінних середовища, ніколи в файлах `config/*.yaml`, що комітяться в систему контролю версій - Тестуйте правила безпеки функціональними тестами `WebTestCase`, що верифікують як дозволений, так і заборонений доступ - Аудитуйте залежності за допомогою `symfony security:check` в CI pipeline'ах --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/symfony/symfony-rest-api-security-oauth2-rate-limiting-2026