Safeguard
Application Security•

ASVS 5.0 V10.4.7: Harden Unauthenticated Dynamic Client Registration

If anyone can register an OAuth client, you must validate its metadata, get user consent, and warn about untrusted apps. Practical controls and tests.

Safeguard Security Team
3 min read

ASVS 5.0 V10.4.7 (Level 2) applies only if your authorization server lets clients register themselves without authentication, which is what RFC 7591 open registration does. In that case you must validate the client metadata, make sure the user consents, and warn the user before processing an authorization request from an untrusted client.

If you do not offer open dynamic registration, this requirement is not applicable, and you should record that decision.

Why it matters

With open registration, an attacker can create a client called "Company IT Support" with a logo and a redirect URI they control, then send a victim a link. If the consent screen looks routine, the victim approves and the attacker's server receives a code.

The metadata is attacker supplied, so nothing in it can be trusted: the name, the logo, the redirect URIs, the policy and terms links. Each one is also an injection or phishing vector if you render it carelessly.

MCP servers made this topic common, since clients often register on the fly with servers they have never met.

How to implement it

Validate everything at registration time:

  • Redirect URIs must be absolute, use https (or loopback and private use schemes for native apps), contain no fragment, and have no wildcards.
  • Reject metadata fields that point to internal addresses, to avoid server side request forgery when you fetch logos or JWKS documents.
  • Cap field lengths and sanitize the display name. Escape it when you render the consent page.
  • Rate limit registration per source and set an expiry on clients that never complete a flow.
from urllib.parse import urlparse

def valid_redirect_uri(uri):
    u = urlparse(uri)
    if u.fragment or "*" in uri:
        return False
    if u.scheme == "https" and u.hostname:
        return True
    if u.scheme == "http" and u.hostname in ("127.0.0.1", "[::1]"):
        return True
    return False

On the consent screen, mark dynamically registered clients as unverified. Show the redirect host, state that the app is not known to you, and never skip the prompt for them, even if the user approved a similar client before.

How to test it

Register a client with a redirect URI of http://evil.example/cb, then one with a wildcard, then one with a fragment. All should be rejected. Register a client whose name contains markup and confirm the consent page shows it as text. Register ten clients rapidly and confirm throttling. Start an authorization request from a new dynamic client and confirm an explicit warning and consent prompt appear.

Common mistakes

  • Treating a registered client name as an identity.
  • Auto approving consent for clients that registered five minutes ago.
  • Fetching client supplied URLs without SSRF protection.
  • Never cleaning up stale clients.

Quick check

What is the user warned about?

That the requesting application is untrusted, because anyone could have registered it.

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.