0%

Origins and Cross-Site Requests

Sending Is Not Reading

A page can show an image that lives on any other site. It can link to any other site, and it can hold a form that posts to any other site. The web was built that way. Each of those is a request that one site’s page starts and another site’s server answers.

Your model can already say which such a request carries. This lesson is about the two decisions around that one.

Three questions

For any request a page starts, a browser answers three questions, and it answers them separately.

a page                                                a server
  |
  |   1. Is the request sent?        Almost always.
  |   2. Does the cookie go along?   Host, path, Secure, SameSite.
  |
  | ------------------ the request -------------------> |
  |                                                     |  the server
  |                                                     |  acts
  | <----------------- the response ------------------- |
  |
  |   3. May the page read the response?
  |      Only when it comes from the page's own origin.

The first question is whether the request is sent at all. The answer is almost always yes. The is a kind of request that Chapter 9 covers.

The second is whether a cookie goes along. That is Lesson 3: the host, the path, Secure, and SameSite, which compares sites.

The third comes after the server has acted and answered: may the page that started the request read the response? This one compares .

The policy is about reading

The rule behind the third question is the : a page’s script may read a response only when it comes from the page’s own origin. It may not read another origin’s page, its , or its headers.

Read that sentence again and look for the word “send”. It is not there. The policy says what a page may read. Nothing in it stops the request from leaving, and nothing in it mentions cookies. Chapter 9 shows how a server can give another origin permission to read.

The full policy covers more than responses. It also has rules for frames, for data a page stores in the browser, and for scripts loaded from another origin. This lesson stays with responses.

The case table

Here are six requests to https://reading.example. In each case the jar holds Ana’s session cookie, which is SameSite=Lax.

The page is onIt startsSent?Cookie?May the page read the response?
other.examplean imageyesnono
other.examplea form postyesnono
other.examplea followed linkyesyesno
notes.reading.examplean imageyesyesno
notes.reading.examplea form postyesyesno
reading.examplea script’s fetchyesyesyes

In the third row the reader ends up on the reading and sees its page. The page that held the link does not: its script reads nothing.

The next block prints the same table. Every request is sent, so it has no column for that. The cookie column is computed by a short copy of the Lax rule you wrote, and the last column by comparing origins. Run it. Then change a row, for example the of the second one to "GET", and run it again.

Try it

A page on https://other.example contains an image whose address is https://reading.example/me. What happens?

The same image sits on a page of https://notes.reading.example. What changes?

What follows

Look at the fifth row once more. A page on notes.reading.example posts a form to the reading list. The request is sent. Ana’s cookie goes along, because the neighbor is on the same site. The server sees a POST with a valid and acts on it. The page that sent the form never gets to read the response.

That page does not need the response. If the form said “delete book 1”, the book was gone before any response existed.

A page cannot read the response to a form it posted to another origin. Why can the post still do harm?

So the same-origin policy protects what your server says, and it does not protect what your server does. A request that rides on someone’s cookie to make a change they never asked for is called , or CSRF. The answers in this lesson are the model’s, and nothing was sent while you read it. In the next lesson you forge such a request against your own server.