mirror of
https://github.com/Hopiu/angular.js.git
synced 2026-05-01 19:54:45 +00:00
fix($http): only set X-XSFR-TOKEN header for same-domain request
This is needed to prevent CORS preflight checks. The XSFR token is quite useless for CORS requests anyway. BREAKING CHANGE: X-XSFR-TOKEN is no longer send for cross domain requests. This shouldn't affect any known production service. Closes #1096
This commit is contained in:
parent
3a75b1124d
commit
fce100a46c
2 changed files with 73 additions and 2 deletions
|
|
@ -29,6 +29,43 @@ function parseHeaders(headers) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var IS_SAME_DOMAIN_URL_MATCH = /^(([^:]+):)?\/\/(\w+:{0,1}\w*@)?([\w\.-]*)?(:([0-9]+))?(.*)$/;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a request and location URL and determine whether this is a same-domain request.
|
||||||
|
*
|
||||||
|
* @param {string} requestUrl The url of the request.
|
||||||
|
* @param {string} locationUrl The current browser location url.
|
||||||
|
* @returns {boolean} Whether the request is for the same domain.
|
||||||
|
*/
|
||||||
|
function isSameDomain(requestUrl, locationUrl) {
|
||||||
|
var match = IS_SAME_DOMAIN_URL_MATCH.exec(requestUrl);
|
||||||
|
// if requestUrl is relative, the regex does not match.
|
||||||
|
if (match == null) return true;
|
||||||
|
|
||||||
|
var domain1 = {
|
||||||
|
protocol: match[2],
|
||||||
|
host: match[4],
|
||||||
|
port: int(match[6]) || DEFAULT_PORTS[match[2]] || null,
|
||||||
|
// IE8 sets unmatched groups to '' instead of undefined.
|
||||||
|
relativeProtocol: match[2] === undefined || match[2] === ''
|
||||||
|
};
|
||||||
|
|
||||||
|
match = URL_MATCH.exec(locationUrl);
|
||||||
|
var domain2 = {
|
||||||
|
protocol: match[1],
|
||||||
|
host: match[3],
|
||||||
|
port: int(match[5]) || DEFAULT_PORTS[match[1]] || null
|
||||||
|
};
|
||||||
|
|
||||||
|
return (domain1.protocol == domain2.protocol || domain1.relativeProtocol) &&
|
||||||
|
domain1.host == domain2.host &&
|
||||||
|
(domain1.port == domain2.port || (domain1.relativeProtocol &&
|
||||||
|
domain2.port == DEFAULT_PORTS[domain2.protocol]));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a function that provides access to parsed headers.
|
* Returns a function that provides access to parsed headers.
|
||||||
*
|
*
|
||||||
|
|
@ -345,7 +382,7 @@ function $HttpProvider() {
|
||||||
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
|
* to counter XSRF. When performing XHR requests, the $http service reads a token from a cookie
|
||||||
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
|
* called `XSRF-TOKEN` and sets it as the HTTP header `X-XSRF-TOKEN`. Since only JavaScript that
|
||||||
* runs on your domain could read the cookie, your server can be assured that the XHR came from
|
* runs on your domain could read the cookie, your server can be assured that the XHR came from
|
||||||
* JavaScript running on your domain.
|
* JavaScript running on your domain. The header will not be set for cross-domain requests.
|
||||||
*
|
*
|
||||||
* To take advantage of this, your server needs to set a token in a JavaScript readable session
|
* To take advantage of this, your server needs to set a token in a JavaScript readable session
|
||||||
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
|
* cookie called `XSRF-TOKEN` on first HTTP GET request. On subsequent non-GET requests the
|
||||||
|
|
@ -476,7 +513,9 @@ function $HttpProvider() {
|
||||||
var reqTransformFn = config.transformRequest || defaults.transformRequest,
|
var reqTransformFn = config.transformRequest || defaults.transformRequest,
|
||||||
respTransformFn = config.transformResponse || defaults.transformResponse,
|
respTransformFn = config.transformResponse || defaults.transformResponse,
|
||||||
defHeaders = defaults.headers,
|
defHeaders = defaults.headers,
|
||||||
reqHeaders = extend({'X-XSRF-TOKEN': $browser.cookies()['XSRF-TOKEN']},
|
xsrfToken = isSameDomain(config.url, $browser.url()) ?
|
||||||
|
$browser.cookies()['XSRF-TOKEN'] : undefined,
|
||||||
|
reqHeaders = extend({'X-XSRF-TOKEN': xsrfToken},
|
||||||
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
|
defHeaders.common, defHeaders[lowercase(config.method)], config.headers),
|
||||||
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
reqData = transformData(config.data, headersGetter(reqHeaders), reqTransformFn),
|
||||||
promise;
|
promise;
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,17 @@ describe('$http', function() {
|
||||||
$httpBackend.flush();
|
$httpBackend.flush();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not set XSRF cookie for cross-domain requests', inject(function($browser) {
|
||||||
|
$browser.cookies('XSRF-TOKEN', 'secret');
|
||||||
|
$browser.url('http://host.com/base');
|
||||||
|
$httpBackend.expect('GET', 'http://www.test.com/url', undefined, function(headers) {
|
||||||
|
return headers['X-XSRF-TOKEN'] === undefined;
|
||||||
|
}).respond('');
|
||||||
|
|
||||||
|
$http({url: 'http://www.test.com/url', method: 'GET', headers: {}});
|
||||||
|
$httpBackend.flush();
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
it('should not send Content-Type header if request data/body is undefined', function() {
|
it('should not send Content-Type header if request data/body is undefined', function() {
|
||||||
$httpBackend.expect('POST', '/url', undefined, function(headers) {
|
$httpBackend.expect('POST', '/url', undefined, function(headers) {
|
||||||
|
|
@ -1005,4 +1016,25 @@ describe('$http', function() {
|
||||||
|
|
||||||
$httpBackend.verifyNoOutstandingExpectation = noop;
|
$httpBackend.verifyNoOutstandingExpectation = noop;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('isSameDomain', function() {
|
||||||
|
it('should support various combinations of urls', function() {
|
||||||
|
expect(isSameDomain('path/morepath',
|
||||||
|
'http://www.adomain.com')).toBe(true);
|
||||||
|
expect(isSameDomain('http://www.adomain.com/path',
|
||||||
|
'http://www.adomain.com')).toBe(true);
|
||||||
|
expect(isSameDomain('//www.adomain.com/path',
|
||||||
|
'http://www.adomain.com')).toBe(true);
|
||||||
|
expect(isSameDomain('//www.adomain.com/path',
|
||||||
|
'https://www.adomain.com')).toBe(true);
|
||||||
|
expect(isSameDomain('//www.adomain.com/path',
|
||||||
|
'http://www.adomain.com:1234')).toBe(false);
|
||||||
|
expect(isSameDomain('https://www.adomain.com/path',
|
||||||
|
'http://www.adomain.com')).toBe(false);
|
||||||
|
expect(isSameDomain('http://www.adomain.com:1234/path',
|
||||||
|
'http://www.adomain.com')).toBe(false);
|
||||||
|
expect(isSameDomain('http://www.anotherdomain.com/path',
|
||||||
|
'http://www.adomain.com')).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue