Rutba
Start free

Blog · How we build

The password was right. The browser refused the last hop

A sign-in that looked like a wrong password was a security header doing its job too well: it allowed the form but not the page the form lands on. The fix was to build that rule from the list of places a sign-in is already allowed to end.

· 3 min read

Share this

Here is a bug report that tells you nothing: “I sign in, and it puts me back on the sign-in page.” No error, no message, no wrong-password warning. Just the same form again, with a retry marker in the address bar.

The password had been accepted. What failed was the last hop — the redirect that carries you from the sign-in service to whichever console sent you there — and it was refused by the browser, on our instruction.

Our sign-in pages ship a deliberately tight content security policy: no scripts, no images, no frames, nothing embedded. One line in it says where a form on the page may submit. We had set it to “this site only”, which reads as the safe answer and is correct for a form that posts back to itself.

Except that rule does not only cover where the form posts. It covers where the submission ends up after any redirects. Every sign-in we serve is designed to end somewhere else — the console you came from, or your own organization’s installation. So the browser took the password, followed our redirect, and refused at the door of the destination, reporting a policy violation against a form whose action was our own origin. From the outside, that is indistinguishable from a sign-in that failed.

The policy was not wrong about forms. It was wrong about journeys, and a sign-in is a journey.

The list already existed

The right fix was not to loosen the rule. It was to notice that we had already written down, exactly once, the set of places a sign-in may legitimately end: the return-to allow-list. That list is an allow-list of exact origins, never a pattern, for a reason worth repeating:

  • A check that an address “ends with rutba.io” accepts evil-rutba.io.
  • A pattern with an unescaped dot accepts rutbaXio.
  • Comparing a parsed address’s origin against a fixed set has no such edges.

Why so careful about where a sign-in may send you? Because a login form that will forward you anywhere is worth more to a phisher than a stolen password. The link genuinely starts at our address, the padlock is genuinely ours, and the victim arrives on a page the attacker controls having just been taught that this is where they sign in.

So the browser policy is now built from that same list. Nothing is permitted that was not already trusted to receive somebody after they sign in — and an origin missing from the list is now refused twice rather than once: by our own check, and by the browser.

What we take from it

Two things. First, a security control that makes a correct sign-in look like a wrong password is a control that will be turned off by somebody in a hurry; the version that survives is the one built from a list the system already maintains for the same purpose. Second, the failure was invisible to every check that asks “did the password work?”, because it did. Only walking the whole journey — password, redirect, landing — finds this class of fault, so both halves of the fix are pinned by tests: the header we send, and the list it is built from.

Trust at Rutba

How sign-in works across every Rutba site, and what we publish when it does not.

Read the trust page

Read next

How we build

Every page links the family the first time it says the name

Nobody hand-links a marketing site consistently, and a site with no internal links makes readers use a search engine to find its own pages. So the prose is structured content now, and the first mention of a product in any sentence becomes a link.

3 min read

Money

One price list, and no page carries a price

One product was advertised at three different prices depending on which Rutba site you opened, and nothing could tell. Every figure now comes from one record as the page renders — and a test fails the build if anybody types a price into the site.

3 min read

Share this

One family

The rest of Rutba

One account across all of it. Sign in once and the products know each other.

PORTAL-BLOG-DETAIL · 7649699