Alguien hace que tu API realice trabajo caro sin límite. No siempre es un ataque. A menudo es un bucle con un bug.
Aquí: un listado con per_page=1000000, o per_page=-1. Una petición por lotes con diez mil elementos. Una exportación de todo, pedida cada minuto. Un cuerpo de petición de cincuenta megabytes.
Qué lo impide: cada entrada de esta API tiene un límite.
| Entrada | Límite | Capítulo |
|-------|-------|---------|
| Peticiones por minuto | por consumidor | 6 |
| Peticiones por segundo | por dirección, en el servidor web | 6 |
| per_page | de 1 a 100 | 5 |
| search | 100 caracteres | 5, 18 |
| webhook_url | 255 caracteres | 18 |
| Elementos en una petición por lotes | 100 | 2 |
| since en una exportación | 90 días hacia atrás | 18 |
| Exportaciones | una por consumidor cada diez minutos | 18 |
| contact_email | 254 caracteres | 5 |
| Idempotency-Key, X-Trace-Id | un UUID o nada | 8, 7 |
| Cuerpo de la petición | el servidor web, y post_max_size de PHP | |
| Llamada al proveedor | 10 segundos, 3 intentos | 3 |
Cómo lo sabes: recorre cada FormRequest y busca una regla sin límite superior. Un string sin max. Un array sin max. Un integer sin between. Cada uno es una entrada cuyo costo lo decide quien llama. Después haz lo mismo con las cabeceras que leen tus propios middleware, que ningún FormRequest cubre.
A esta API todavía le falta un límite, y una revisión debería decirlo. La respuesta de un proveedor no tiene límite de tamaño. Se confía en que Statamic no envíe un gigabyte, y el timeout de diez segundos es lo único que lo detendría.
5. Autorización rota a nivel de función
Quien llama alcanza una operación que no debería tener: un consumidor corriente que llama al endpoint de un operador.
Aquí: un consumidor de solo lectura que llama a DELETE /api/licenses/{license}, o que crea cien licencias a través del endpoint de lotes. Cualquier consumidor que llama a POST /api/consumers. Un consumidor que emite un token con más habilidades de las que tiene.
Qué lo impide: todo controlador declara quién puede llamarlo. LicenseController, LicenseBatchController y ExportController exigen una habilidad de licencias. ConsumerController y TokenController exigen la del operador. Y HeldByCaller en el Capítulo 5 significa que quien llama solo puede conceder habilidades que tiene.
La debilidad es la palabra «todo». Un controlador que no declara nada está protegido solo por auth:sanctum, que cualquier consumidor pasa. Laravel no puede saber que se te olvidó. Así es exactamente como el endpoint de lotes del borrador del Capítulo 18 dejaba escribir a los tokens de solo lectura.
Cómo lo sabes: la tabla del Capítulo 12, con una fila por ruta y por llamante al que hay que rechazar, ejecutada como un test. Es tediosa de escribir. También es la página más valiosa que produce una revisión. Una tabla no puede fallar por una ruta que nadie listó, así que añade la fila en el mismo pull request que la ruta, y convierte eso en una línea de tu lista de comprobación de revisión. (Documentación de Laravel: Sanctum › Token Abilities.)