Skip to content

Throttle FranceConnect introspections triggered by unknown bearers - #485

Merged
skelz0r merged 2 commits into
developfrom
feature/fc-introspection-throttle
Oct 7, 2026
Merged

skelz0r merged 2 commits into
developfrom
feature/fc-introspection-throttle

Conversation

@skelz0r

@skelz0r skelz0r commented Oct 6, 2026

Copy link
Copy Markdown
Member

Any bearer sent to a FranceConnectable endpoint (v3 *_with_france_connect and the legacy v2 cnav, mesri and cnous ones) triggers a POST to the FranceConnect introspection URL with our client credentials. Without a resolved user, existing throttles are keyed on the token hash, so an anonymous caller rotating random bearers gets a fresh quota on every request, holds a Puma thread and spends our FranceConnect client_id quota.

These requests are now throttled per IP (30/min, 600/h) and globally (120/min). Production usage over the last 30 days peaks at 7/min and 166/h for a single IP and 9/min globally, legitimate callers being a handful of partner backends (~960 requests/day). The protective throttles are left out of the RateLimit headers, which otherwise disappear and raise a Sentry warning when two throttles match.

429 is already documented on these endpoints and handled by the SDKs, no client change needed. Upstream protections (nginx, WAF) are not visible from the repository and were not assumed.

Any bearer sent to a FranceConnectable endpoint (v3 *_with_france_connect
and the legacy v2 ones) triggers a POST to the FranceConnect token
introspection URL with our client credentials. Without a resolved user,
the existing throttles are keyed on the token hash, so an anonymous
caller rotating random bearers gets a fresh quota on every request while
holding a Puma thread and spending our FranceConnect client_id quota.

Requests carrying a bearer that does not resolve to a user are now also
throttled by IP on these endpoints: 30 per minute and 600 per hour.
Production usage over the last 30 days peaks at 7 per minute and 166
per hour for a single IP, legitimate callers being partner backends.

These protective throttles are left out of the RateLimit headers so the
advertised quota stays the per-authorization one, and so legitimate
FranceConnect calls do not trigger the multiple throttles warning.
The per IP throttle does not stop an attacker spreading random bearers
over many IPs, who could still saturate the Puma workers shared by API
Entreprise and API Particulier, or get our client_id limited by
FranceConnect.

Introspections triggered by a bearer that does not resolve to a user are
now capped at 120 per minute across all callers. Production peaks at 9
per minute globally over the last 30 days. During a distributed attack
legitimate FranceConnect users get 429 too, which is preferred over
exhausting the workers of both APIs.
@skelz0r skelz0r self-assigned this Oct 6, 2026
@skelz0r
skelz0r requested a review from Un3x October 6, 2026 14:43
@skelz0r
skelz0r merged commit c20b97e into develop Oct 7, 2026
9 checks passed
@skelz0r
skelz0r deleted the feature/fc-introspection-throttle branch October 7, 2026 11:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants