Exact-Match Validation Before Token Issuance

A small redirect bug, three services, and why startsWith is not a security check.

Single sign-on flows usually end the same way: the user logs in, and the identity provider sends them back to the app that asked, along with a session. The URL they’re sent back to is often just a query parameter. In our case, nobody was checking that parameter, and that meant anyone could receive a user’s tokens by sending them a link.

This post covers how we found the gap, why the obvious fixes don’t work, and the pattern we used instead: approve the destination before creating the token, using exact matching on a canonical URL.

Background

Our internal platform has its own login page (Google sign-in or an email OTP). Other internal apps use it for SSO. They send users to the login page with a redirect_uri, and after login the user lands on the app’s callback page with a session:

/login?redirect_uri=

Three components are involved. The web app (React) hosts the login page. The identity service (Spring Boot) verifies users and creates access and refresh tokens. The Core API (Spring Boot) holds admin-managed settings.

The problem

During a review of the login flow, we traced what happens to redirect_uri. The frontend’s handling came down to this:

if (searchParams.get('redirect_uri') && searchParams.get('source')) {
navigate(`/sso/success?eid=${searchParams.get('redirect_uri')}`, {
state: { accessToken, refreshToken, email, name },
});
}

Whatever URL arrived in the query string received the new session. An attacker does not need direct entry or breach into our infrastructure to cause harm. They only need a victim to click a link.

The victim logs in on our real page, and our own frontend hands the session to the attacker.
Figure 1. The victim logs in on our real page, and our own frontend hands the session to the attacker.

This is an open redirect, a well-known weakness (OWASP has a cheat sheet on it). Here it’s more serious than usual, because the redirect carries credentials with it.

Why nobody noticed

  • Every login worked: A redirect flaw is invisible as long as everyone uses legitimate links.
  • It looked guarded: The source parameter reads like a gate, but the code only checked that it was present. Adding &source=x gets past it.
  • Nobody owned the decision: The frontend chose the destination, the identity service created the tokens, and the Core API held the configuration. Each could reasonably assume someone else was validating the URL.

Tracing further showed three code paths that ended in an SSO redirect: Google sign-in, OTP sign-in, and a silent token refresh when an already logged-in user opens an SSO link. The last one never shows a login form, so a fix that only covered the form would have missed it.

That also answered where the fix belongs. Tokens are created in the identity service, so the destination must be approved there, before the token exists. A check in the browser runs after the tokens have been issued.

Why the obvious fixes fail

The first fix most people reach for is a string check. Suppose the approved callback is :

Prefix and substring checks let attacker URLs through yet reject harmless variants of the real URLPrefix and substring checks let attacker URLs through yet reject harmless variants of the real URL
Figure 2: Prefix and substring checks let attacker URLs through, yet reject harmless variants of the real URL.
  • startsWith accepts app.example.com.evil.com (a different host) and app.example.com@evil.com, where everything before @ is a username and evil.com is the real host.
  • contains accepts our host hidden in someone else’s path or query string.
  • Suffix or regex rules trust forgotten subdomains, and an unescaped . in a regex matches any character.

String checks also reject things they shouldn’t: HTTPS://APP.example.com/… is the same URL, but a case-sensitive prefix check refuses it. The OAuth 2.0 security best-practice document (RFC 9700) is clear on this: redirect URIs should be matched exactly, never by pattern.

PakarPBN

A Private Blog Network (PBN) is a collection of websites that are controlled by a single individual or organization and used primarily to build backlinks to a “money site” in order to influence its ranking in search engines such as Google. The core idea behind a PBN is based on the importance of backlinks in Google’s ranking algorithm. Since Google views backlinks as signals of authority and trust, some website owners attempt to artificially create these signals through a controlled network of sites.

In a typical PBN setup, the owner acquires expired or aged domains that already have existing authority, backlinks, and history. These domains are rebuilt with new content and hosted separately, often using different IP addresses, hosting providers, themes, and ownership details to make them appear unrelated. Within the content published on these sites, links are strategically placed that point to the main website the owner wants to rank higher. By doing this, the owner attempts to pass link equity (also known as “link juice”) from the PBN sites to the target website.

The purpose of a PBN is to give the impression that the target website is naturally earning links from multiple independent sources. If done effectively, this can temporarily improve keyword rankings, increase organic visibility, and drive more traffic from search results.

Jasa Backlink

Download Anime Batch