Skip to content

Ajax.Request#isSameOrigin() treats protocol-relative URLs as same-origin #369

Closed as duplicate

Description

@knogineer

While going through the Ajax code in src/prototype/ajax/request.js I noticed that isSameOrigin() classifies a protocol-relative URL (for example //evil.example/x.js) as same-origin, even though it points at a different origin. Since isSameOrigin() is one of the gates that decides whether a response body gets evaluated as JavaScript, this lets a cross-origin response slip past that check.

Here is the relevant code (around line 323):

isSameOrigin: function() {
  var m = this.url.match(/^\s*https?:\/\/[^\/]*/);
  return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({
    protocol: location.protocol,
    domain: document.domain,
    port: location.port ? ':' + location.port : ''
  }));
},

The regex only matches an absolute http:// or https:// prefix. For any URL that does not start with one of those, m is null, so !m short-circuits to true and the URL is treated as same-origin. That is correct for genuinely relative URLs like /api/x, but it also captures protocol-relative URLs like //host/x, which are cross-origin.

Reproducing just the logic:

function sameOriginMatch(url){ return url.match(/^\s*https?:\/\/[^\/]*/); }
['//evil.com/x.js','https://evil.com/x.js','/local/x.js'].forEach(function(u){
  console.log(u, '=> treated same-origin:', !sameOriginMatch(u));
});
// //evil.com/x.js       => treated same-origin: true   (wrong, this is cross-origin)
// https://evil.com/x.js => treated same-origin: false
// /local/x.js           => treated same-origin: true   (correct)

Why it matters: combined with the default evalJS: true, a request whose URL is built from data that can begin with // can end up executing an attacker-controlled response in the page's origin, because evalResponse() runs the response body through eval() when the content type looks like JavaScript.

Suggested fix: resolve the URL against location (for example new URL(this.url, location.href)) and compare .origin against location.origin, so protocol-relative URLs are treated as potentially cross-origin. It would also be worth dropping document.domain from the comparison, since it can be relaxed at runtime.

Refs: CWE-346 (origin validation error), OWASP ASVS V5.2.4.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions