SameSite and forged requests
Why it matters Cookies are sent automatically, which is convenient for you and, without care, convenient for attackers too. This lab shows the classic trick that SameSite was invented to stop, on a practice bank, and then you switch the defence on yourself.
Imagine someone slips a signed form into a pile of papers on your desk, and you hand the whole pile to the bank without reading it. The bank sees your signature and does what the form says. Nobody stole your signature; they just got you to carry their request.
Forged request (CSRF): Cross-Site Request Forgery: a page on one site makes your browser send a request to another site where you are logged in. Because your browser attaches your cookie automatically, the other site thinks you asked for it.
The practice attack, step by step
Everything here is fictional: bank.example and prizes.example are practice sites.
- You log in to
bank.example. It setsSet-Cookie: session=s-55; SameSite=None; Secure. - Later you open
prizes.example, which says "You have won! Click to claim." - Behind the button is a hidden form that sends a POST to
bank.example/transferwith "to: prize-account, amount: 500". - Your browser sends it, and because the cookie says SameSite=None, it attaches
session=s-55. The bank sees a logged-in request and carries it out.
GET and POST: the two most common kinds of request. GET means "fetch me this page". POST means "here is a form, please act on it", such as sending money or changing a password.
Top-level request: a request that moves your whole tab to a new site, like clicking a link. A picture or advert loading inside a page is not top-level.
The defence
SameSite lets the bank say when its cookie may travel with a request that started on another site:
Strict: never. Safest, but if you click a link to the bank from an email, you arrive looking logged out.Lax: only for a top-level GET, such as clicking a link. A hidden POST from prizes.example arrives with no cookie, so the bank refuses it.None: always. Needed by some embeds, but it leaves the door open to this trick.
Real banks add a second lock as well: a secret CSRF token hidden in their own forms, which a page on another site cannot know. SameSite is the first lock; tokens are the second.
Your first lines of Python
You will write a small function, a named recipe that takes some inputs and gives back an answer. You only need three pieces of Python, all shown here:
def is_big(n): # "def" starts a function called is_big, with one input n
if n > 10: # "if" checks a condition; the indented lines run only when it is true
return True # "return" hands back the answer and stops
return False
print(is_big(12)) # True
print("a" == "a") # == asks "are these equal?" and gives True or False
print(True and False) # "and" is True only when both sides are True: here FalseA function returns as soon as it hits a return, so checking rules in order, top to bottom, works naturally.
Two more things you will see in the starter. Text in quotes, such as "GET" or "bank.example", is called a string: Python compares strings letter for letter, so "Lax" == "lax" is False. And a block of text between three quote marks right under a def line is a note describing the function; Python does not run it.
In other languages
Setting the bank's cookie with SameSite=Lax in other languages:
// JavaScript (Node.js with Express)
res.cookie("session", "s-55", { secure: true, httpOnly: true, sameSite: "lax" });
# Python (Django settings.py)
SESSION_COOKIE_SAMESITE = "Lax"
SESSION_COOKIE_SECURE = True
// Go (net/http)
http.SetCookie(w, &http.Cookie{Name: "session", Value: "s-55",
Secure: true, HttpOnly: true, SameSite: http.SameSiteLaxMode})
// PHP 7.3 or later
setcookie("session", "s-55", ["secure" => true, "httponly" => true, "samesite" => "Lax"]);
// C# (ASP.NET Core)
Response.Cookies.Append("session", "s-55", new CookieOptions {
Secure = true, HttpOnly = true, SameSite = SameSiteMode.Lax });Recap
- A forged request makes your browser carry someone else's request to a site where you are logged in.
- It works because cookies are attached automatically, and SameSite=None allows that from any site.
- SameSite=Lax blocks cross-site POSTs but keeps normal link clicks working; Strict blocks even those.
- Real sites pair SameSite with CSRF tokens for a second lock.
Write the function should_send(samesite, from_site, to_site, method, top_level) so it returns True or False by these rules, checked in order: (1) if from_site == to_site, return True; (2) if samesite is "None", return True; (3) if samesite is "Lax", return True only when top_level is True and method is "GET"; (4) otherwise return False. Then set bank_samesite to the setting that blocks the forged POST from prizes.example but still sends the cookie when you click a link to the bank from mail.example. Keep the two lines that compute forged_post_sent and link_click_sent.
This lesson is locked
Lessons open one at a time. Finish the previous lesson to unlock this one.