# Keamanan REST API Symfony di 2026: OAuth2, Rate Limiting, dan Pertanyaan Interview > Pelajari cara mengamankan REST API Symfony dengan OAuth2 token introspection, rate limiting, dan validasi JWT. Membahas fitur keamanan Symfony 7.3, kerentanan umum, dan pertanyaan interview teknis. - Published: 2026-09-14 - Updated: 2026-09-14 - Author: Anthony Fillion-Maillet - Tags: symfony, security, oauth2, rest-api, rate-limiting, jwt - Reading time: 10 min --- Keamanan REST API Symfony membutuhkan pendekatan berlapis yang menggabungkan autentikasi, otorisasi, dan kontrol lalu lintas. Symfony 7.3 memperkenalkan dukungan native OAuth2 token introspection melalui RFC 7662, sementara komponen RateLimiter menyediakan perlindungan bawaan terhadap penyalahgunaan. > **Lapisan Keamanan Utama untuk API Symfony** > > API Symfony yang siap produksi membutuhkan tiga pertahanan: autentikasi (siapa yang memanggil), otorisasi (apa yang dapat diakses), dan rate limiting (seberapa sering). Mengabaikan salah satu lapisan akan mengekspos aplikasi terhadap credential stuffing, eksfiltrasi data, atau denial of service. ## OAuth2 Token Introspection di Symfony 7.3 Symfony 7.3 menambahkan dukungan bawaan untuk [OAuth2 Token Introspection (RFC 7662)](https://datatracker.ietf.org/doc/html/rfc7662). Fitur ini memvalidasi access token dengan melakukan query langsung ke authorization server, menghilangkan kebutuhan untuk mendekode token secara lokal. Ini penting ketika format token bersifat opaque atau dikontrol oleh identity provider pihak ketiga. Konfigurasi `AccessTokenHandler` menerima 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)%' ``` Response introspection berisi `active`, `scope`, `client_id`, dan opsional `username`. Symfony memetakan ini ke atribut keamanan secara otomatis. ```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 { // Token sudah divalidasi melalui introspection $user = $this->getUser(); return $this->json([ 'id' => $user->getId(), 'email' => $user->getEmail(), 'scopes' => $user->getRoles(), ]); } } ``` Pendekatan ini menghilangkan beban verifikasi signature JWT dari aplikasi. Authorization server menangani rotasi key, pencabutan token, dan validasi scope. ## Implementasi Rate Limiting dengan Komponen RateLimiter [Komponen RateLimiter](https://symfony.com/doc/current/rate_limiter.html) melindungi endpoint dari penyalahgunaan menggunakan algoritma token bucket atau sliding window. Konfigurasi dilakukan di `framework.yaml`: ```yaml # config/packages/framework.yaml framework: rate_limiter: # Rate limit API umum: 100 request per menit api_limiter: policy: sliding_window limit: 100 interval: '1 minute' # Limit lebih ketat untuk endpoint autentikasi login_limiter: policy: token_bucket limit: 5 rate: { interval: '1 minute', amount: 5 } ``` Terapkan rate limiting ke controller menggunakan 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(); // Hanya terapkan ke route API if (!str_starts_with($request->getPathInfo(), '/api')) { return; } // Gunakan IP client atau user terautentikasi sebagai limiter key $key = $request->getClientIp(); $limiter = $this->apiLimiter->create($key); if (!$limiter->consume()->isAccepted()) { throw new TooManyRequestsHttpException(); } } } ``` Untuk API terautentikasi, ganti key berbasis IP dengan identifier user untuk mencegah satu user yang menyalahgunakan mempengaruhi traffic legitimate dari jaringan yang sama. ## Validasi JWT Tanpa Dependensi Eksternal Ketika authorization server menerbitkan JWT dengan public key yang diketahui, Symfony dapat memvalidasi token secara lokal menggunakan `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 ``` Untuk JWT yang diterbitkan sendiri tanpa provider OIDC, gunakan `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 { // Decode dan validasi signature secara otomatis $payload = $this->jwtManager->parse($accessToken); return new UserBadge($payload['sub']); } } ``` Bundle JWT menangani verifikasi signature RS256/ES256, pengecekan expiration, dan validasi issuer. Simpan public key di `config/jwt/public.pem` dan referensikan di `lexik_jwt_authentication.yaml`. ## Mengamankan Endpoint API dengan Voters Symfony Voters menyediakan otorisasi fine-grained di luar pengecekan role sederhana. Pola umum memeriksa kepemilikan resource: ```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; // Admin dapat melakukan apa saja if (in_array('ROLE_ADMIN', $user->getRoles(), true)) { return true; } // Penulis dapat mengedit/menghapus artikel mereka sendiri return $article->getAuthor() === $user; } } ``` Gunakan voter di controller: ```php #[Route('/api/articles/{id}', methods: ['PUT'])] public function update(Article $article, Request $request): JsonResponse { $this->denyAccessUnlessGranted(ArticleVoter::EDIT, $article); // Logika update di sini } ``` Voters memusatkan logika otorisasi dan membuatnya dapat diuji. Mereka juga sering muncul dalam [pertanyaan interview Symfony](/technologies/symfony/interview-questions/events-subscribers) tentang komponen keamanan. ## Kerentanan API Umum dan Mitigasinya [OWASP API Security Top 10](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) mengidentifikasi kerentanan yang berulang dalam API. Symfony menyediakan perlindungan bawaan untuk beberapa di antaranya: | Kerentanan | Mitigasi Symfony | |------------|------------------| | Broken Object Level Authorization | Voters dengan pengecekan kepemilikan | | Broken Authentication | Login throttling, secure password hashing | | Excessive Data Exposure | Grup serialisasi DTO | | Lack of Rate Limiting | Komponen RateLimiter | | Mass Assignment | Validasi form, mapping DTO | | Security Misconfiguration | Security checker Symfony, env vars | Untuk perlindungan mass assignment, jangan pernah melakukan hydrate entity langsung dari data request: ```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, // Sengaja dihilangkan: author, createdAt, status // Ini di-set oleh aplikasi, bukan client ) {} } ``` Pendekatan whitelist DTO mencegah client mengatur field yang tidak seharusnya mereka kontrol, seperti `isAdmin` atau `createdAt`. ## Konfigurasi CORS untuk Consumer API Header Cross-Origin Resource Sharing mengontrol domain mana yang dapat memanggil API dari browser. Bundle `nelmio/cors-bundle` menyediakan konfigurasi deklaratif: ```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 ``` Hindari `allow_origin: ['*']` dengan `allow_credentials: true`. Kombinasi ini mengekspos API terhadap pencurian kredensial melalui situs berbahaya. Gunakan whitelist domain eksplisit atau pola regex. ## Pertanyaan Interview Teknis tentang Keamanan API Symfony Pertanyaan-pertanyaan ini sering muncul dalam interview developer Symfony senior. Jawaban mencerminkan kemampuan Symfony 7.3. **T: Bagaimana Symfony menangani validasi access token OAuth2?** Symfony 7.3 mendukung tiga pendekatan: validasi JWT lokal dengan verifikasi signature, panggilan endpoint user info OIDC, dan [token introspection RFC 7662](https://symfony.com/blog/new-in-symfony-7-3-security-improvements). Introspection cocok untuk token opaque atau skenario di mana aplikasi tidak mengontrol authorization server. `AccessTokenHandler` mengabstraksi ketiganya di balik interface yang seragam. **T: Apa perbedaan antara firewall dan access control di Symfony?** Firewall mendefinisikan mekanisme autentikasi per pola URL. Aturan access control mendefinisikan persyaratan otorisasi setelah autentikasi berhasil. Firewall mungkin membutuhkan JWT yang valid, sementara access control membutuhkan `ROLE_ADMIN` untuk `/api/admin/*`. Mereka beroperasi secara berurutan: firewall melakukan autentikasi, kemudian access control melakukan otorisasi. **T: Bagaimana cara mencegah serangan brute force pada endpoint login?** Login throttling bawaan Symfony membatasi percobaan gagal per username dan IP. Konfigurasikan `login_throttling` di firewall: ```yaml security: firewalls: main: login_throttling: max_attempts: 5 interval: '15 minutes' ``` Untuk autentikasi API, kombinasikan dengan komponen RateLimiter pada endpoint token. Simpan hitungan percobaan di Redis untuk deployment multi-instance. **T: Jelaskan Symfony Voters dan kapan menggunakannya dibanding pengecekan role sederhana.** Voters mengimplementasikan logika otorisasi kompleks yang bergantung pada resource, bukan hanya role user. Gunakan voters ketika: user harus memiliki resource, resource memiliki state machine (draft vs published), atau otorisasi bergantung pada aturan bisnis (tier subscription). Pengecekan role cukup untuk permission statis seperti "hanya admin yang dapat mengakses settings." Untuk pertanyaan keamanan Symfony lainnya, lihat [panduan persiapan interview Symfony](/blog/symfony/symfony-interview-questions). ## Membangun API Symfony yang Aman: Poin-Poin Penting - Konfigurasikan OAuth2 token introspection untuk identity provider pihak ketiga di mana format token bersifat opaque - Terapkan rate limiting di level infrastruktur (reverse proxy) dan level aplikasi (komponen RateLimiter) untuk defense in depth - Gunakan Voters untuk otorisasi level resource, cadangkan pengecekan role untuk permission statis - Petakan data request ke DTO dengan properti eksplisit untuk mencegah mass assignment - Simpan secret di environment variable, jangan pernah di file `config/*.yaml` yang di-commit ke version control - Uji aturan keamanan dengan functional test `WebTestCase` yang memverifikasi akses yang diizinkan dan ditolak - Audit dependency dengan `symfony security:check` di pipeline CI --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/id/blog/symfony/symfony-rest-api-security-oauth2-rate-limiting-2026