# Seguridad de API REST en Symfony 2026: OAuth2, Rate Limiting y Preguntas de Entrevista > Guia completa sobre seguridad de API REST en Symfony con OAuth2, rate limiting, JWT y Voters. Incluye preguntas de entrevista tecnica. - Published: 2026-09-14 - Updated: 2026-09-14 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- La seguridad de APIs REST en Symfony requiere un enfoque por capas que combine autenticacion, autorizacion y control de trafico. Symfony 7.3 introduce soporte nativo para introspection de tokens OAuth2 mediante RFC 7662, mientras que el componente RateLimiter proporciona proteccion integrada contra abusos. > **Tres capas de seguridad para APIs Symfony** > > Una API Symfony lista para produccion necesita tres defensas: autenticacion (quien llama), autorizacion (a que puede acceder) y rate limiting (con que frecuencia). La ausencia de cualquier capa expone la aplicacion a credential stuffing, exfiltracion de datos o denegacion de servicio. ## Introspection de Tokens OAuth2 en Symfony 7.3 Symfony 7.3 agrega soporte nativo para [introspection de tokens OAuth2 (RFC 7662)](https://datatracker.ietf.org/doc/html/rfc7662). Esta funcionalidad valida tokens de acceso consultando directamente al servidor de autorizacion, eliminando la necesidad de decodificar tokens localmente. Este enfoque resulta especialmente util cuando el formato del token es opaco o esta controlado por un proveedor de identidad externo. La configuracion de `AccessTokenHandler` acepta un endpoint de 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)%' ``` La respuesta de introspection contiene `active`, `scope`, `client_id` y opcionalmente `username`. Symfony mapea estos valores automaticamente a los atributos de seguridad. ```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 ya validado via introspection $user = $this->getUser(); return $this->json([ 'id' => $user->getId(), 'email' => $user->getEmail(), 'scopes' => $user->getRoles(), ]); } } ``` Este enfoque libera a la aplicacion de la verificacion de firmas JWT. El servidor de autorizacion maneja la rotacion de claves, revocacion de tokens y validacion de scopes. ## Implementacion de Rate Limiting con el Componente RateLimiter El [componente RateLimiter](https://symfony.com/doc/current/rate_limiter.html) protege los endpoints contra abusos utilizando algoritmos token bucket o sliding window. La configuracion se realiza en `framework.yaml`: ```yaml # config/packages/framework.yaml framework: rate_limiter: # Limite general de API: 100 requests por minuto api_limiter: policy: sliding_window limit: 100 interval: '1 minute' # Limite mas estricto para endpoints de autenticacion login_limiter: policy: token_bucket limit: 5 rate: { interval: '1 minute', amount: 5 } ``` El rate limiting se aplica a los controladores mediante un 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(); // Aplicar solo a rutas API if (!str_starts_with($request->getPathInfo(), '/api')) { return; } // Usar IP del cliente o usuario autenticado como clave $key = $request->getClientIp(); $limiter = $this->apiLimiter->create($key); if (!$limiter->consume()->isAccepted()) { throw new TooManyRequestsHttpException(); } } } ``` Para APIs autenticadas, conviene reemplazar la clave basada en IP por el identificador del usuario para evitar que un usuario abusivo afecte el trafico legitimo proveniente de la misma red. ## Validacion JWT Sin Dependencias Externas Cuando el servidor de autorizacion emite JWTs con una clave publica conocida, Symfony puede validar tokens localmente usando `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 ``` Para JWTs auto-emitidos sin proveedor OIDC, el bundle `lexik/jwt-authentication-bundle` ofrece una solucion robusta: ```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 { // Decodifica y valida la firma automaticamente $payload = $this->jwtManager->parse($accessToken); return new UserBadge($payload['sub']); } } ``` El bundle JWT maneja la verificacion de firmas RS256/ES256, controles de expiracion y validacion del emisor. La clave publica se almacena en `config/jwt/public.pem` y se referencia en `lexik_jwt_authentication.yaml`. ## Seguridad de Endpoints API con Voters Los Voters de Symfony proporcionan autorizacion granular mas alla de simples verificaciones de roles. Un patron comun verifica la propiedad del recurso: ```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; // Los admins pueden hacer todo if (in_array('ROLE_ADMIN', $user->getRoles(), true)) { return true; } // Los autores pueden editar/eliminar sus propios articulos return $article->getAuthor() === $user; } } ``` El uso del voter en controladores se realiza asi: ```php #[Route('/api/articles/{id}', methods: ['PUT'])] public function update(Article $article, Request $request): JsonResponse { $this->denyAccessUnlessGranted(ArticleVoter::EDIT, $article); // Logica de actualizacion aqui } ``` Los Voters centralizan la logica de autorizacion y la hacen testeable. Aparecen frecuentemente en las [preguntas de entrevista Symfony](/technologies/symfony/interview-questions/events-subscribers) sobre el componente de seguridad. ## Vulnerabilidades Comunes de API y Mitigaciones El [OWASP API Security Top 10](https://owasp.org/API-Security/editions/2023/en/0x11-t10/) identifica vulnerabilidades recurrentes en APIs. Symfony proporciona proteccion integrada para varias de ellas: | Vulnerabilidad | Mitigacion Symfony | |---------------|--------------------| | Broken Object Level Authorization | Voters con verificacion de propiedad | | Broken Authentication | Throttling de login, hashing seguro | | Excessive Data Exposure | Grupos de serializacion DTO | | Lack of Rate Limiting | Componente RateLimiter | | Mass Assignment | Validacion de formularios, mapeo DTO | | Security Misconfiguration | Security checker, variables de entorno | Para proteccion contra mass assignment, nunca se deben hidratar entidades directamente desde datos del 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, // Intencionalmente omitido: author, createdAt, status // Estos valores los define la aplicacion, no el cliente ) {} } ``` El enfoque de whitelist mediante DTO previene que los clientes establezcan campos que no deberian controlar, como `isAdmin` o `createdAt`. ## Configuracion CORS para Consumidores de API Los headers Cross-Origin Resource Sharing controlan que dominios pueden llamar a la API desde navegadores. El bundle `nelmio/cors-bundle` proporciona configuracion declarativa: ```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 ``` Se debe evitar `allow_origin: ['*']` con `allow_credentials: true`. Esta combinacion expone la API al robo de credenciales mediante sitios maliciosos. Las whitelists de dominios explicitas o patrones regex son preferibles. ## Preguntas de Entrevista Tecnica sobre Seguridad API Symfony Estas preguntas aparecen frecuentemente en entrevistas para desarrolladores Symfony senior. Las respuestas reflejan las capacidades de Symfony 7.3. **P: Como maneja Symfony la validacion de tokens de acceso OAuth2?** Symfony 7.3 soporta tres enfoques: validacion JWT local con verificacion de firma, llamadas al endpoint user info de OIDC, e [introspection de token RFC 7662](https://symfony.com/blog/new-in-symfony-7-3-security-improvements). La introspection es adecuada para tokens opacos o escenarios donde la aplicacion no controla el servidor de autorizacion. `AccessTokenHandler` abstrae los tres detras de una interfaz unificada. **P: Cual es la diferencia entre firewalls y access control en Symfony?** Los firewalls definen mecanismos de autenticacion por patron de URL. Las reglas de access control definen requisitos de autorizacion despues de que la autenticacion tiene exito. Un firewall puede requerir un JWT valido, mientras que access control requiere `ROLE_ADMIN` para `/api/admin/*`. Operan en secuencia: el firewall autentica, luego access control autoriza. **P: Como prevenir ataques de fuerza bruta en un endpoint de login?** El throttling de login integrado de Symfony limita intentos fallidos por nombre de usuario e IP. La configuracion de `login_throttling` se realiza en el firewall: ```yaml security: firewalls: main: login_throttling: max_attempts: 5 interval: '15 minutes' ``` Para autenticacion de API, se debe combinar con el componente RateLimiter en el endpoint de token. Los contadores de intentos se almacenan en Redis para despliegues multi-instancia. **P: Explique los Voters de Symfony y cuando usarlos en lugar de simples verificaciones de roles.** Los Voters implementan logica de autorizacion compleja que depende del recurso, no solo del rol del usuario. Se deben usar Voters cuando: el usuario debe ser propietario del recurso, el recurso tiene una maquina de estados (borrador vs publicado), o la autorizacion depende de reglas de negocio (nivel de suscripcion). Las verificaciones de roles son suficientes para permisos estaticos como "solo los admins pueden acceder a configuraciones". Para mas preguntas de seguridad Symfony, consulte la [guia de preparacion para entrevistas Symfony](/blog/symfony/symfony-interview-questions). ## Construyendo una API Symfony Segura: Puntos Clave - Configurar introspection de tokens OAuth2 para proveedores de identidad externos cuando el formato del token es opaco - Aplicar rate limiting a nivel de infraestructura (reverse proxy) y a nivel de aplicacion (componente RateLimiter) para defensa en profundidad - Usar Voters para autorizacion a nivel de recurso, reservando verificaciones de roles para permisos estaticos - Mapear datos del request a DTOs con propiedades explicitas para prevenir mass assignment - Almacenar secretos en variables de entorno, nunca en archivos `config/*.yaml` versionados - Probar reglas de seguridad con tests funcionales `WebTestCase` que verifiquen accesos permitidos y denegados - Auditar dependencias con `symfony security:check` en pipelines de CI --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/es/blog/symfony/symfony-rest-api-security-oauth2-rate-limiting-2026