Skip to content

Security

Fixing Open Redirects without guessing

Stop deleting next parameters from muscle memory. Confirm the public Location on a host you control, enforce a real destination policy, and verify the same probe goes quiet.

· by Henry Smith

Fixing Open Redirects without guessing

I still reach for curl first. Before I rip out a next query "just to be safe," I want to see the Location header on the live host. Guessing the fix from a blog post is how you break post-login UX and still leave an open redirect on a legacy path nobody remembered.

SlaySlop's Open redirects check "Checks whether redirects can be steered to untrusted destinations." Fixing without guessing means reproducing that steer on a host you control, changing the real policy layer, and verifying the same probe no longer lands off-brand.

Outcome you want

After the fix, intended continue flows still work. Untrusted absolute destinations no longer appear in Location. A follow-up scan no longer flags the same Open redirects evidence on those routes. Redirect chain cleanup stays on its own ticket if hops are messy for other reasons.

Boundaries before you edit

Read-only scanner. You change your own app, gateway, or CDN rules. Permission required on anything you scan. Not a pentest. Not a license to test third-party IdPs as if they were yours. Open redirects is one Security row; CORS and frame protection still get their own triage.

Loading diagram.

Happy path: reproduce, constrain, verify

  1. Copy the affected URL from the finding evidence.
  2. Send a relative destination and an absolute untrusted destination.
  3. Confirm which layer sets Location: app, IdP config you own, API gateway, CDN.
  4. Change one layer at a time toward relative-only, signed return tokens, or an explicit host allowlist.
  5. Purge CDN cache if that layer was involved.
  6. Re-probe both destination shapes.
  7. Rescan the same host in SlaySlop.
bash
# Permissioned verification loop
LOGIN="https://app.example.com/auth/continue"
probe() {
  local next="$1"
  echo "next=$next"
  curl -sI "$LOGIN?next=$next" \
    | awk 'BEGIN{IGNORECASE=1} /^HTTP|^location:/{print}'
  echo
}
probe "/app/home"
probe "https://untrusted.example/land"

I want the first probe to show an on-host continue (or a deliberate partner allowlist hit). I want the second probe to refuse the external destination without reflecting it. If product needs a partner return, name the partner host in config with an owner. Do not invent a regex that "mostly" matches.

Illustrative policy shape (syntax varies; idea does not):

text
return_url_policy: allowlist
allowed_return_hosts:
  - app.example.com
  - partner.example.com
allow_relative_paths: true
reject_other_absolute_urls: true

Ship the real syntax your stack uses. The point is an explicit rule, not a Friday night deletion of every query parameter.

Pitfalls

Fixing staging only. Fixing the SPA router while the server still issues Location. Fixing login but not password-reset continue. Fixing by stripping next entirely so users always land on / and open a ticket about "broken deep links." I have done the strip version. It feels decisive. Support queues disagree.

Another trap: treating the finding as a reason to disable redirects altogether and push everything through JavaScript. That can hide the header from a casual glance and still leave a server-side open redirect on an API route. Keep the mechanism honest.

Verify both directions

After tightening, confirm the intended continue still works and the untrusted probe stays rejected. Teams often verify only the negative probe and ship a broken first-party flow, or verify only the SPA and leave reflection in place. Both directions, same hour, same commit in the ticket.

If a partner needs temporary access, issue a dated allowlist entry with an owner and a removal ticket already filed. Temporary without a ticket is permanent. I still check with curl when the removal date arrives.

When the finding is intentional

Some public shorteners and documented partner handoffs are supposed to leave your host. Do not "fix" those into broken campaigns. Document the intent, constrain the host list, and move on. The check asks whether redirects can be steered to untrusted destinations. For a deliberate partner hop, "untrusted" is the wrong word if the partner is named and reviewed.

The guessing failure mode is deleting every continue parameter. The engineering failure mode is assuming none of them are dangerous. Read the evidence. Then change the policy.