Skip to content

Instantly share code, notes, and snippets.

@aiwilliams
Last active December 19, 2015 19:49
Show Gist options
  • Save aiwilliams/6008955 to your computer and use it in GitHub Desktop.
Save aiwilliams/6008955 to your computer and use it in GitHub Desktop.
Exploring CORS on api.github.com.

Be sure to test the 08 html document using the http:// protocol.

01-05 were requests made to api.github.com using curl. I wanted to see what the response headers looked like, where:

  • 01 - Simple GET with no credentials.
  • 02 - Simple GET with Basic (username:password).
  • 03 - Simple GET with Basic, my own user - the response certainly included private information about me, the authenticated user.
  • 04 - Creating an OAUTH token - do these, too, have Access-Control-* headers? Yes!
  • 05 - Simple GET with Authorization token.

06-09 demonstrate a CORS request from an HTML document on my hard drive (08), loaded by Chrome (28.0.1500.71), where:

  • 06 - A GET request to github.com, to discover whether there were any Access-Control-* response headers. Nope, none.
  • 07 - A GET request to api.github.com. This one includes the Access-Control-* response headers. Notice that some of the cookies went with this request.
  • 08 - The file I opened in Chrome to make the XHR CORS request.
  • 09 - An XHR GET request. Be sure to load the HTML document through a server so that you are using the http:// protocol, otherwise the Origin header will be 'null' when loaded in the browser from a file:// URL. The Access-Control-* response headers are there, however, be sure to notice that the request did NOT include any cookies. This is because I did not invoke .withCredentials = true on the XHR request.

Allowing all API endpoints to respond with Access-Control-Allow-Origin: * is not a problem, as long as no one actually attempts to use .withCredentials = true when they make their call. In that case, the response must also include Access-Control-Allow-Credentials: true and Access-Control-Allow-Origin cannot be *, but should instead be a single value matching the requesting host. The browser will consider the request a failure.

It is interesting to note that responses from api.github.com have this header combination:

Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *

Reading the spec here, you'll find this Note in the discussion about supports credentials:

The string "*" cannot be used (as the value of the Access-Control-Allow-Origin header) for a resource that supports credentials.

My concern is further expressed in the discussion about Security Concerns. Namely, if a resource supports credentials, it should also reflect the requesting Origin as the Access-Control-Allow-Origin.

When Chrome makes a .withCredentials XHR request, it will fail the response if it has Access-Control-Allow-Origin: *, delaring the requirement that the request Origin must be the value.

When .withCredentials = true, the cookies which go with the CORS request should be well defined. In working to understand whether HttpOnly session cookies will go, I found that they do indeed. This means that HttpOnly really is about accessing the values from JavaScript, not about preventing it from being sent to it's owning domain when using XHR requests.

> GET /users/defunkt HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: api.github.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: GitHub.com
< Date: Tue, 16 Jul 2013 13:32:50 GMT
< Content-Type: application/json; charset=utf-8
< Status: 200 OK
< X-RateLimit-Limit: 60
< X-RateLimit-Remaining: 58
< X-RateLimit-Reset: 1373985159
< Vary: Accept
< Cache-Control: public, max-age=60, s-maxage=60
< Last-Modified: Tue, 16 Jul 2013 11:53:14 GMT
< ETag: "cb0416c253be8cbcd50f1f5ee4d3ff05"
< X-GitHub-Media-Type: github.beta
< X-Content-Type-Options: nosniff
< Content-Length: 1395
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Access-Control-Allow-Origin: *
< Vary: Accept-Encoding
<
> GET /users/defunkt HTTP/1.1
> Authorization: Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: api.github.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: GitHub.com
< Date: Tue, 16 Jul 2013 13:39:33 GMT
< Content-Type: application/json; charset=utf-8
< Status: 200 OK
< X-RateLimit-Limit: 5000
< X-RateLimit-Remaining: 4998
< X-RateLimit-Reset: 1373985528
< Vary: Accept, Authorization, Cookie
< Cache-Control: private, max-age=60, s-maxage=60
< Last-Modified: Tue, 16 Jul 2013 11:53:14 GMT
< ETag: "c0ca5f674df556e35bfa4ee495177348"
< X-GitHub-Media-Type: github.beta
< X-Content-Type-Options: nosniff
< Content-Length: 1395
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Access-Control-Allow-Origin: *
< Vary: Accept-Encoding
<
> GET /users/aiwilliams HTTP/1.1
> Authorization: Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXX
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: api.github.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: GitHub.com
< Date: Tue, 16 Jul 2013 13:42:09 GMT
< Content-Type: application/json; charset=utf-8
< Status: 200 OK
< X-RateLimit-Limit: 5000
< X-RateLimit-Remaining: 4997
< X-RateLimit-Reset: 1373985528
< Vary: Accept, Authorization, Cookie
< Cache-Control: private, max-age=60, s-maxage=60
< Last-Modified: Mon, 15 Jul 2013 14:48:19 GMT
< ETag: "8e9d05ee92846e1a5fb0d06eededac8f"
< X-GitHub-Media-Type: github.beta
< X-Content-Type-Options: nosniff
< Content-Length: 1664
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Access-Control-Allow-Origin: *
< Vary: Accept-Encoding
<
> POST /authorizations HTTP/1.1
> Authorization: Basic XXXXXXXXXXXXXXXXXXXXXXXXXXXX
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: api.github.com
> Accept: */*
> Content-Length: 20
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 20 out of 20 bytes
< HTTP/1.1 201 Created
< Server: GitHub.com
< Date: Tue, 16 Jul 2013 13:45:35 GMT
< Content-Type: application/json; charset=utf-8
< Status: 201 Created
< X-RateLimit-Limit: 5000
< X-RateLimit-Remaining: 4996
< X-RateLimit-Reset: 1373985528
< Location: https://api.github.com/authorizations/3071011
< X-GitHub-Media-Type: github.beta
< X-Content-Type-Options: nosniff
< Content-Length: 438
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Access-Control-Allow-Origin: *
< ETag: "4bccf59b46eed92229d33082b31232a8"
< Cache-Control: max-age=0, private, must-revalidate
<
> GET /user HTTP/1.1
> User-Agent: curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8x zlib/1.2.5
> Host: api.github.com
> Accept: */*
> Origin: http://example.com
> Authorization: token XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
>
< HTTP/1.1 200 OK
< Server: GitHub.com
< Date: Tue, 16 Jul 2013 13:52:13 GMT
< Content-Type: application/json; charset=utf-8
< Status: 200 OK
< X-RateLimit-Limit: 5000
< X-RateLimit-Remaining: 4992
< X-RateLimit-Reset: 1373985528
< Vary: Accept, Authorization, Cookie
< Cache-Control: private, max-age=60, s-maxage=60
< Last-Modified: Mon, 15 Jul 2013 14:48:19 GMT
< ETag: "983a916e897862cdf5d67d1f253c51a7"
< X-OAuth-Scopes: repo
< X-Accepted-OAuth-Scopes: user, user:email, user:follow, site_admin
< X-GitHub-Media-Type: github.beta
< X-Content-Type-Options: nosniff
< Content-Length: 1409
< Access-Control-Allow-Credentials: true
< Access-Control-Expose-Headers: ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Access-Control-Allow-Origin: *
< Vary: Accept-Encoding
<
> GET / HTTP/1.1
> User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36
> Host:github.com
> Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
> Accept-Encoding:gzip,deflate,sdch
> Accept-Language:en-US,en;q=0.8
> Cache-Control:max-age=0
> Connection:keep-alive
> Cookie:_gauges_unique=1; tracker=direct; spy_user=aiwilliams; logged_in=yes; dotcom_user=aiwilliams; _gh_sess=XXXXXXXXX
> If-None-Match:"29f899762217eb191c1415394208ee46"
> Referer:https://github.com/login
>
< Cache-Control:private, max-age=0, must-revalidate
< Connection:keep-alive
< Content-Encoding:gzip
< Content-Security-Policy:default-src *; script-src 'self' https://github.global.ssl.fastly.net https://jobs.github.com https://ssl.google-analytics.com https://collector.githubapp.com https://gist.github.com; style-src 'self' 'unsafe-inline' https://github.global.ssl.fastly.net; object-src 'self' https://github.global.ssl.fastly.net
< Content-Type:text/html; charset=utf-8
< Date:Tue, 16 Jul 2013 14:58:41 GMT
< ETag:"3698791c5eeea3da80ddb9d1a0e6693b"
< Server:GitHub.com
< Set-Cookie:dotcom_user=aiwilliams; domain=.github.com; path=/; expires=Sat, 16-Jul-2033 14:58:41 GMT; secure; HttpOnly
< Set-Cookie:spy_user=aiwilliams; path=/; expires=Sat, 16-Jul-2033 14:58:41 GMT; HttpOnly
< Set-Cookie:logged_in=yes; domain=.github.com; path=/; expires=Sat, 16-Jul-2033 14:58:41 GMT; secure; HttpOnly
< Set-Cookie:_gh_sess=XXXXXXXX; path=/; expires=Sun, 01-Jan-2023 00:00:00 GMT; secure; HttpOnly
< Status:200 OK
< Strict-Transport-Security:max-age=2592000
< Transfer-Encoding:chunked
< Vary:Accept-Encoding
< X-Frame-Options:deny
< X-Runtime:68
> GET /user HTTP/1.1
> User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36
> Host:api.github.com
> Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
> Accept-Encoding:gzip,deflate,sdch
> Accept-Language:en-US,en;q=0.8
> Cache-Control:max-age=0
> Connection:keep-alive
> Cookie:logged_in=yes; dotcom_user=aiwilliams
>
< Access-Control-Allow-Credentials:true
< Access-Control-Allow-Origin:*
< Access-Control-Expose-Headers:ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Content-Length:43
< Content-Type:application/json; charset=utf-8
< Date:Tue, 16 Jul 2013 15:04:23 GMT
< Server:GitHub.com
< Status:401 Unauthorized
< X-Content-Type-Options:nosniff
< X-GitHub-Media-Type:github.beta
< X-RateLimit-Limit:60
< X-RateLimit-Remaining:54
< X-RateLimit-Reset:1373990466
<!DOCTYPE HTML>
<html lang="en-us">
<head>
<title>Github API JavaScript Test</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
</head>
<body>
<script>
function createCORSRequest(method, url) {
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr) {
// XHR for Chrome/Firefox/Opera/Safari.
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined") {
// XDomainRequest for IE.
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
// CORS not supported.
xhr = null;
}
return xhr;
}
function makeCorsRequest() {
var url = 'https://api.github.com/user';
var xhr = createCORSRequest('GET', url);
if (!xhr) {
alert('CORS not supported');
return;
}
// Response handlers.
xhr.onload = function() {
var text = xhr.responseText;
console.log('Response from CORS request to ' + url);
console.log(text);
};
xhr.onerror = function() {
alert('Woops, there was an error making the request.');
};
xhr.send();
}
makeCorsRequest();
</script>
</body>
</html>
> GET /user HTTP/1.1
> User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.71 Safari/537.36
> Host:api.github.com
> Accept:*/*
> Accept-Encoding:gzip,deflate,sdch
> Accept-Language:en-US,en;q=0.8
> Cache-Control:max-age=0
> Connection:keep-alive
> Origin:null
>
< Access-Control-Allow-Credentials:true
< Access-Control-Allow-Origin:*
< Access-Control-Expose-Headers:ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes
< Content-Length:43
< Content-Type:application/json; charset=utf-8
< Date:Tue, 16 Jul 2013 15:01:06 GMT
< Server:GitHub.com
< Status:401 Unauthorized
< X-Content-Type-Options:nosniff
< X-GitHub-Media-Type:github.beta
< X-RateLimit-Limit:60
< X-RateLimit-Remaining:59
< X-RateLimit-Reset:1373990466
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment