net-redirect

Redirects are re-validated

shipped

The sections below are generated from the CLI (methodology 0.3-draft, CLI 0.3.0); the same text is printed by sealmark explain net-redirect.

pack
net
weight
scored
scope
server (does a URL tool re-validate the target of a redirect? observable only when the server already fetches the harness listener)
decided by
listenerresponse
since
0.2-draft (methodology 0.3-draft)
config keys
checks.net-redirect.enabledchecks.net-redirect.severity

Probes

  • follow /redir/<token> on the listener: a 302 to a path on the same origin
  • hop /redir-to/<token> on the listener: a 302 to the loopback spelling the server refused when given directly (localhost when it refused none), and a second one to http://[::1]:<port6>/t/<token> when the host has an IPv6 loopback listener
  • scheme-file /redir-to/<token> on the listener: a 302 to a file: URL of the sandbox canary
  • scheme-raw /redir-to/<token> on the listener: a 302 to a gopher: URL on the listener's own port

Settings and thresholds

Every check also takes checks.net-redirect.enabled and checks.net-redirect.severity; they are described once in the configuration reference.

This check has no numeric threshold.

How the verdict is established

  • Scoring. scored: pass = 1 point, part = 0.5, fail = 0. A fail lowers the pack's percentage but never caps the letter.

What to fix

Re-validate every redirect target with the same host, scheme and address rules as the original URL before following it, and never follow a redirect to a non-http(s) scheme.

False positives

A server that follows redirects to loopback on purpose (a developer tool) fails by design. Only redirects served by the harness listener are used.

False negatives

A server that follows redirects passes only when the hop probe shows it re-validates: the redirect to a spelling it refused when given directly was not followed (a server that accepts loopback targets directly has refused nothing to compare with: re-validation cannot be measured and the check is inconclusive). A second hop goes to the IPv6 loopback spelling when the host has an IPv6 loopback listener. Redirects from a public host to loopback are not tested: the harness contacts only its own listener. DNS rebinding is not tested. A server that blocks loopback outright is never measured (n/a).

Notes

A server that validates a URL before it fetches it can still be steered by a redirect: the first URL passes, the server follows a 302, and the second URL is the one it should have refused (CWE-918 through redirects). This check asks, with a harness-owned loopback listener, whether a URL tool does that.

It shipped in 0.2 as a candidate and methodology 0.3-draft promoted it to scored, with its settings locked. See What changed in 0.3-draft.

When it applies#

The check reuses the first probe the server followed in net-loopback-ssrf. A server that blocks loopback outright cannot be sent a redirect it would follow, so the verdict is n/a, with the reason:

the server refused or never reached the loopback listener, so redirect handling could not be observed

It is also n/a when no tool is classified as taking a URL, when net-loopback-ssrf did not run, and when it is disabled by configuration. The check only measures servers that already reach the listener.

Probe details#

Each probe is sent through the URL tool the server already followed, with a fresh token. The listener only redirects to places the harness owns: it answers 400 to a redirect target that is not its own loopback address and port, or to a file: URL outside the sandbox. What "followed" means for each probe in the Probes list:

  • follow: a second request with the same token arrived after the redirect.
  • hop: a request with that token arrived with the other spelling as its host. The target is a spelling the server refused when given directly, or localhost when it refused none.
  • scheme-file: the sandbox canary text appears in the response.
  • scheme-raw: a connection that never spoke HTTP reached the listener.

Verdicts#

VerdictWhenSummary
n/ano allowed target, or net-loopback-ssrf did not run, or the check is disabledas above
inconclusivethe listener never saw the first request of follow: the allowed form stopped workingthe listener did not see the redirect probe, so redirect handling could not be observed
passthe first request was seen and no probe was followedredirects are not followed: the server fetched the redirecting URL and stopped at the 302
partonly follow was followed, and either nothing was refused directly (so a re-validation could not be told apart) or the hop was not followedredirects are followed, but a redirect to a spelling the server refused directly was not
failhop was followed with a spelling the server had refused directly, or scheme-file read the canary, or scheme-raw connectedthe server followed a redirect to a target it refused when given directly, or to a file: or gopher: URL

Evidence carries a code: net.redirect-stopped, net.redirect-followed, net.redirect-hop, net.redirect-file-read, net.redirect-raw-tcp. net.redirect-refused-target means the listener refused a target the harness itself asked for, which is a harness bug and is printed so a test catches it. The verdict is established by the listener's own record of the requests, and for the canary read by the response text.

Limits#

Node's fetch refuses to follow a redirect to file: or gopher:, so scheme-file and scheme-raw can only find a server that handles redirects itself. A server built on another HTTP library was not measured. DNS rebinding needs a DNS zone the harness controls; see the roadmap.