0%

Chapter 9

CORS

Permission to Read

Chapter 8 was about a request that another page should not be able to make. This chapter starts from the opposite wish.

The reading is getting a second front end, at https://app.reading.example. It is one of the four names from Chapter 8, and it is yours. A script on its pages wants to send GET https://reading.example/about and show the answer.

The two hosts are one site and two . You know what follows from Lesson 4 of Chapter 8: the request is sent, the server answers, and the keeps the answer from the page. That rule protected ana from the neighbor’s pages. Here it stops a page that you wrote yourself.

The server may say yes

The same-origin policy has one , and the server controls it. A response may carry a header that names an origin:

access-control-allow-origin: https://app.reading.example

With that header the server says: a page on this origin may read this response. The arrangement is called , for cross-origin resource sharing. For a plain GET it takes four steps.

the page on           the browser         the server at
app.reading.example                       reading.example

1. asks for GET /about
   ----------------->
                      2. sends the request, and adds
                         origin: https://app.reading.example
                         --------------->
                                          3. answers as always,
                                             and may add
                                             access-control-allow-origin
                         <---------------
                      4. looks at that header, and hands
   <---------------      the response over, or keeps it
  1. The page’s script asks its browser for the request.

  2. The browser sends it. Because the request goes to another origin, the browser adds an Origin header with the page’s origin. The page cannot change that header.

  3. The server answers as it always does. If it wants to, it adds Access-Control-Allow-Origin.

  4. The browser looks at that one header. It hands the response to the script only when the header names the page’s origin, or when it is *, which stands for every origin. In every other case the script gets nothing.

Look at who acts in each step. The server gives the permission in the third step. The browser enforces it in the fourth. The page has no say at all.

The Origin header of the second step is the one your server already reads. In Chapter 8 it used that header to refuse a forged POST. Now the same header tells the server which page is asking, so that it can answer that page by name.

Trace it

The next block is the fourth step: a response in the shape your server returns, and the check a browser makes. Run it. Then take the header out of the dict, change it to "*", or change PAGE to "https://notes.reading.example", and run it again each time.

Try it

This block is a sketch of the rule, and it sends nothing. In Lesson 2 you write the real check into mini_browser.py.

Who decides, on each request, whether a page may read a response from another origin?

What the server did before the browser decided

By the fourth step the server has finished. It received the request, ran handle(), and sent the whole response. When the browser refuses the read, nothing is taken back. The response arrived, and it was withheld from a script.

So a missing header never undoes anything. If the request changed something on the server, the change is made. That is the lesson of Chapter 8 once more: reading is what the browser guards, and a request that changes something has done so before any header is looked at.

A page on another origin posts a form, and the server acts on it. The response has no Access-Control-Allow-Origin. What is true?

Not a browser, no CORS

Everything above is a promise that browsers keep. wire.py keeps no such promise. It sends the bytes you type and prints every byte that comes back, whatever headers the response has. So does pl_sec.py, and so does any program that someone writes with a .

An Access-Control-Allow-Origin header asks something of browsers only. Leaving it out hides nothing from a program that is not a browser. Keep that in mind through this chapter: whatever must stay secret or unchanged still needs the checks of Chapters 4 to 8, the , the token, the owner of a book, and the Origin check.

wire.py sends GET /about, and the response has no CORS header. Can it read the body?

That is the whole exchange for a plain GET. Two cases are left for later in this chapter: a request that a browser asks about before it sends it, and a request that carries a . In the next lesson you write both halves of the plain case, the server’s answer and the browser’s check.