CORS

Cross-Origin Resource Sharing

GOAL

  • What & Why & How
  • ClientSide
  • ServerSide
  • see it in action

to learn

pre-Game

  • around 2004
  • browser configuration

what was the first browser to support?

Chrome 4.0

  • cons
    • browser spec
    • server configuration*
  • pros
    • users first

*preflight

What does it do?

  • Enables clients to make requests to servers hosted on different origins
  • Client & server side component
    • client controls how CORS request are made
    • server controls which type of CORS requests are allowed (opt-in)

Why do we need it?

  • We are building clientSide applications that live on different origins but need resources from another origin to function
  • 'Security'
    • CORS by itself is NOT security
    • CORS is a very small part in securing your services

How does CORS work?

client =  browser = server

  • Browsers have a Same-Origin Policy
    • contradiction?
  • Servers in control of who & what

How does CORS work?

lifecycle

How does CORS work?

  • Origin request header
    • where client resource lives
    • no Origin header, no CORS
    • scheme, host, port of URL
    • browser sets the Origin header
  • Access-Control-Allow-Origin (ACAO) response header
    • server response
    • no ACAO, no CORS
    • wildcard || Origin value

MUST

How does CORS work?

Preflight Requests (asking for permission)

How does CORS work?

Preflight Requests (asking for permission)

  • preserve backward compatibility
    • existing servers have no idea
  • is it ok to send request?

How NOT to trigger a preflight

  • GET
    • <img />, <script />
  • POST
    • <form />
    • application/x-www-form-urlencoded, multipart/form-data, or text/plain

How does CORS work?

Preflight Requests (asking for permission)

Triggering preflight

  • Method other than GET, POST, or HEAD
  • Content-Type request header with values other than
    • application/x-www-form-urlencoded b multipart/form-data
    • text/plain
  • Additional request headers that are not
    • Accept
    • Accept-Language
    • Content-Language

XMLHttpRequest

onloadstart
onprogress
onabort
onerror
onload
ontimeout
onloadend
onreadystatechange

Events Handlers

Response Properties

status
statusText
response
responseText
responseXML
Cache-Control
Content-Language
Content-Type
Expires
Last-Modified
Pragma

Can only read these Headers*:

*unless server explicitly states otherwise

Cookies are not sent by default*

withCredentials = true;

user credentials such as cookies, 
basic authentication information, or 
Secure Sockets Layer (SSL) certificates

*server must allow cookies

Client Side

XMLHttpRequest

Client Side

fetch('http://localhost:4000/status').then(function(response) {
  return response.json();
}).then(function(stuff) {
  console.log(stuff);
});

GET

fetch('http://localhost:4000/posts').then(function(response) {
  return response.json();
}).then(function(stuff) {
  console.log(stuff);
});

GET

XMLHttpRequest

Client Side

var msg = 'This is another blog post';

fetch('http://localhost:4000/posts', {
  method: 'post', 
  mode: 'cors',
  body: JSON.stringify({
    6: msg
  })
}).then(function(response) {
  return response.json();
}).then(function(stuff) {
  console.log(stuff);
});

POST

fetch('http://localhost:4000/posts/2', {
  method: 'delete'
}).then(function(response) {
  console.log('cool');
})

DELETE

Server Side

const whitelist = [
  config.rootUrl,
  config.ereaderUrl,
  config.ereaderUrlDev,
  config.ereaderUrlTest,
  config.cors.bfwpub,
  config.cors.macmillanHighered,
  config.cors.macmillanTech,
  config.cors.rls
];

const isPreflight = function(req) {
  const isHttpOptions = req.method === 'OPTIONS';
  const hasOriginHeader = req.headers['origin'];
  const hasRequestMethod = req.headers['access-control-request-method'];
  return isHttpOptions && hasOriginHeader && hasRequestMethod;
};

const passThrough = ['GET', 'HEAD'];
const allowOnly = ['POST', 'PUT'];

function allowOrigin(ctx) {
  const { reqId, request } = ctx;

  if (isPreflight(request)) {
    const hasRequestMethod = request.headers['access-control-request-method'];

    if (!allowOnly.includes(hasRequestMethod)) {
      return;
    }
  }

  if (request && request.method && passThrough.includes(request.method)) {
    return '*';
  }

  if (request && request.header && request.header.origin &&
    !whitelist.includes(request.header.origin) && !request.header.origin.match(/\.mldev.cloud/)) {
    logger.error('CORS -- invalid origin', { reqId, origin: request.header.origin });
    ctx.status = 400;
    return;
  }

  return request.header.origin || '*';

In Action...

Made with Slides.com