Safeguard
Application Security•

ASVS 5.0 V10.6.2: Stop Forced Logout Attacks at the OpenID Provider

A logout URL that works on any GET lets any page sign users out. Require user confirmation or validate id_token_hint on every RP-initiated logout.

Safeguard Security Team
3 min read

ASVS 5.0 V10.6.2 (Level 2) requires the OpenID Provider to prevent denial of service through forced logout. Either ask the end user to confirm, or, when the relying party initiates logout, validate parameters in the request such as id_token_hint.

Why it matters

If hitting the end session endpoint signs a user out with no questions, any web page can embed it in an image tag or a redirect and keep users logged out of every app that relies on that provider. It is a nuisance attack, but at scale it is a real outage for users.

How to implement it

On the end session endpoint, check the request before ending anything.

@app.route("/logout", methods=["GET", "POST"])
def end_session():
    hint = request.values.get("id_token_hint")
    if hint:
        claims = verify_id_token_issued_by_us(hint, allow_expired=True)
        client = db.get_client(claims["aud"])
        target = request.values.get("post_logout_redirect_uri")
        if target and target not in client.post_logout_redirect_uris:
            abort(400)
        return end_session_for(claims["sid"], redirect=target)
    return render_template("confirm_logout.html")  # explicit user click, with CSRF token

The confirmation page must submit a POST protected by a CSRF token. Only redirect to registered post logout URIs.

Why the hint matters

The id_token_hint proves that the request refers to a real session issued by this provider. Validate its signature and issuer, and confirm the audience matches a registered client. Accept expired hints, since users often log out long after the token was issued, but check everything else. Use the sid claim to find which session to end, and end only that one, not every session the user has, unless the user chose that.

Redirect handling

The post logout redirect is an open redirect risk. Compare it exactly against the list registered for the client. Reject anything else, and fall back to a plain "you are signed out" page of your own. Never build the target from a parameter without checking.

The confirmation page

When no valid hint arrives, show a page asking "Do you want to sign out?" with a button. That button must submit a POST with a CSRF token tied to the user's session. Without a token, an attacker's page could submit the form for the user and bring the attack back.

<form method="post" action="/logout">
  <input type="hidden" name="csrf" value="{{ csrf_token }}">
  <button type="submit">Sign out</button>
</form>

Back and front channel logout

If your provider notifies relying parties of logout, those notifications are a separate flow with their own checks (see V10.5.5). Do not let the end session endpoint trigger a fan out to many clients on an unauthenticated request, because that multiplies the effect of a forced logout.

How to test it

Load the logout URL from a page on another origin, with no hint. You should see a confirmation prompt, not a silent logout. Send a request with a tampered or foreign id_token_hint and expect rejection. Send an unregistered redirect URI and expect an error.

Monitoring

Count logout requests per source. A sudden spike in confirmation page views or rejected hints is a sign someone is probing. Alert on it, and consider rate limits on the endpoint.

Common mistakes

  • GET logout that acts at once.
  • Treating any JWT as a valid hint.
  • Open redirects through the post logout parameter.

Quick check

Name the two accepted defences.

Explicit confirmation from the user, or validation of request parameters such as the id_token_hint.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.