Cookies and server-side sessions
Objectives
By the end of the lesson, you can describe how a cookie works (Set-Cookie, Cookie), you can tell a cookie from a session, you know the main cookie attributes (Secure, HttpOnly, SameSite, Max-Age), and you understand why logout must also be implemented on the server side.
Why do we need cookies?
According to the previous lesson, the server needs an identifier to tie requests to a user. But where should the identifier come from for every request? The user will not enter it at every click. The cookie automates this: the server sends a small piece of data, the browser stores it, and attaches it by itself to the appropriate later requests. According to MDN, cookies serve three main purposes: session management (login, cart), personalization (language, theme), and tracking.
A new request after login
The student logs in to Kurzuspont. The server checks the login, creates a session, and sends an identifier in the Set-Cookie header of the response. In a later request the browser forwards it in a Cookie request header. The actual response of the trial server and the next request (with curl's cookie jar):
$ curl -i -c jar.txt -X POST http://localhost:8085/login -d '{"nev":"anna","jelszo":"titok1"}'
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: sid=b8734e2e4960707a816d559c00663b42; Path=/; HttpOnly; SameSite=Lax; Max-Age=3600
$ curl -v -b jar.txt http://localhost:8085/jelentkezesek/anna
> GET /jelentkezesek/anna HTTP/1.1
> Cookie: sid=b8734e2e4960707a816d559c00663b42
< HTTP/1.1 200 OK
["webprog"]
sequenceDiagram
participant B as Browser
participant S as Server
B->>S: Login request (name, password)
S-->>B: Set-Cookie: sid=identifier
B->>S: Request courses + Cookie: sid=identifier
S-->>B: Personalized response
It is advisable to keep only the necessary, random identifier in the cookie, and let the server manage the detailed session state. MDN also advises not to put sensitive data directly into the cookie, but to use an opaque session identifier that the server looks up.
The difference between a cookie and a session
- The cookie is the means of transmission between the browser and the server.
- The session is state managed by the application on the server side, which we look up with the identifier.
Consequences: if the server invalidates the session, the old identifier remaining in the browser does not give access again. If the browser loses the cookie, the server cannot connect the next request with the same identifier. A cookie can be used for other purposes too (a language setting), but it is not meant for storing large or complex local data: according to MDN, browsers generally limit the size of a cookie to 4 kilobytes, and a cookie goes along with every matching request, which can slow the connection. For local data, Web Storage or IndexedDB are suitable.
The scope and attributes of a cookie
Besides the name–value pair, a Set-Cookie header can also contain attributes. According to MDN's description:
| Attribute |
What does it do? |
Domain |
Which domain and its subdomains it can be sent to. If not given, only to the original host. |
Path |
The browser sends it only with a request of a matching path (not a security boundary). |
Expires / Max-Age |
How long it lives. If neither is present, it is a session cookie: it is deleted when the browser closes. Max-Age takes precedence. |
Secure |
Sent only with https: requests (localhost is an exception). |
HttpOnly |
JavaScript cannot read it (document.cookie), but the browser attaches it to requests. It protects against XSS (chapter 8). |
SameSite |
Regulates sending in connection with requests that start from another site. |
According to MDN, the values of SameSite are:
Strict: sends it only with requests that start from the same site;
Lax: to the same site, and also with "top-level" navigation started from another site (for example clicking a link) using safe methods. According to MDN's cookie guide this is the default when the attribute is missing;
None: sends it with every request, but needs Secure.
SameSite protects against CSRF (Cross-Site Request Forgery), which chapter 8 discusses: an attacker's page starts a request toward Kurzuspont, and the browser would attach the cookie by itself. Strict and Lax prevent this.
A further protection is the name prefix: a cookie with the __Host- prefix can be set only with Secure, the path Path=/, and without Domain, so it is bound precisely to the given host. The server must check the name together with the prefix.
Third-party cookies and privacy
A third-party cookie belongs to embedded content (for example an iframe or an advertisement) that arrives from another site, and also allows cross-site tracking. According to MDN, browsers increasingly block them by default. The GDPR (General Data Protection Regulation) and the ePrivacy directive ask for transparency and consent for the use of cookies that are not necessary. Lesson 9.6 on privacy discusses this question in detail.
Lifecycle and logout
A session cannot last forever. It can expire, the user can log out, the server can terminate it for security reasons. Logout must therefore also invalidate the server-side access; it is not enough to show the interface as "not logged in". On the trial server, after logout a request sent with the same identifier is rejected:
$ curl -i -b jar.txt -X POST http://localhost:8085/logout
HTTP/1.1 204 No Content
Set-Cookie: sid=; Path=/; Max-Age=0
$ curl -i -H "Cookie: sid=b8734e2e4960707a816d559c00663b42" http://localhost:8085/jelentkezesek/anna
HTTP/1.1 401 Unauthorized
{"hiba":"nincs bejelentkezve"}
Max-Age=0 deletes the cookie on the browser side. What is really important, however, is the second request: the server no longer knows the identifier, so a copy of the old cookie cannot be used either. If logout only "cleaned" the browser, work could continue with an identifier obtained earlier.
MDN gives two further pieces of advice for sessions: the session identifier must be regenerated on login or on entry (against the session fixation attack), and the lifetime of sensitive cookies should be short.
Worked example: the login cookie of Kurzuspont
| Setting |
Value |
Reason |
| Name |
__Host-sid |
Bound to the host, Secure, Path=/. |
| Value |
128-bit random |
Cannot be guessed, carries no data. |
HttpOnly |
yes |
The client program (and a foreign script injected into it) cannot read it. |
Secure |
yes |
Goes only over HTTPS (4.3). |
SameSite |
Lax |
Protects requests started from another site; arriving from a link still works. |
Max-Age |
3600 |
One hour; a short lifetime. |
| Logout |
The server deletes the session |
The old identifier cannot be used. |
(The trial server is simplified, and runs without Secure and the __Host- name prefix, because the example runs over HTTP on localhost.)
Common misconceptions
| Statement |
Clarification |
| "A cookie is the whole session." |
It often carries only the identifier; the state is managed by the server. |
| "The browser sends every cookie with every request." |
The scope and the attributes limit sending. |
| "Logout is just changing the label of a button." |
The server-side access must be terminated too. |
| "All local data should be stored in a cookie." |
Web Storage and IndexedDB are better suited to other purposes. |
"HttpOnly protects against everything." |
It restricts reading related to XSS, but not the attaching to requests. |
In brief
- A cookie is a small piece of data sent by the server, stored and attached by the browser; a session is the server-side state.
- A session cookie should be
HttpOnly, Secure, SameSite, short-lived, with a random identifier.
- Logout must also terminate the server-side session.
- A third-party cookie can be used for tracking; that is why it is regulated.
Review questions
- Which header sends the cookie from the server to the browser, and which sends it back?
- What do
HttpOnly, Secure, and SameSite mean?
- Why is it not enough, at logout, to delete the cookie only from the browser?
- Why is a cookie not good for storing local data?
Learning resources
Cookies and server-side sessions
Objectives
By the end of the lesson, you can describe how a cookie works (
Set-Cookie,Cookie), you can tell a cookie from a session, you know the main cookie attributes (Secure,HttpOnly,SameSite,Max-Age), and you understand why logout must also be implemented on the server side.Why do we need cookies?
According to the previous lesson, the server needs an identifier to tie requests to a user. But where should the identifier come from for every request? The user will not enter it at every click. The cookie automates this: the server sends a small piece of data, the browser stores it, and attaches it by itself to the appropriate later requests. According to MDN, cookies serve three main purposes: session management (login, cart), personalization (language, theme), and tracking.
A new request after login
The student logs in to Kurzuspont. The server checks the login, creates a session, and sends an identifier in the
Set-Cookieheader of the response. In a later request the browser forwards it in aCookierequest header. The actual response of the trial server and the next request (withcurl's cookie jar):sequenceDiagram participant B as Browser participant S as Server B->>S: Login request (name, password) S-->>B: Set-Cookie: sid=identifier B->>S: Request courses + Cookie: sid=identifier S-->>B: Personalized responseIt is advisable to keep only the necessary, random identifier in the cookie, and let the server manage the detailed session state. MDN also advises not to put sensitive data directly into the cookie, but to use an opaque session identifier that the server looks up.
The difference between a cookie and a session
Consequences: if the server invalidates the session, the old identifier remaining in the browser does not give access again. If the browser loses the cookie, the server cannot connect the next request with the same identifier. A cookie can be used for other purposes too (a language setting), but it is not meant for storing large or complex local data: according to MDN, browsers generally limit the size of a cookie to 4 kilobytes, and a cookie goes along with every matching request, which can slow the connection. For local data, Web Storage or IndexedDB are suitable.
The scope and attributes of a cookie
Besides the name–value pair, a
Set-Cookieheader can also contain attributes. According to MDN's description:DomainPathExpires/Max-AgeMax-Agetakes precedence.Securehttps:requests (localhostis an exception).HttpOnlydocument.cookie), but the browser attaches it to requests. It protects against XSS (chapter 8).SameSiteAccording to MDN, the values of
SameSiteare:Strict: sends it only with requests that start from the same site;Lax: to the same site, and also with "top-level" navigation started from another site (for example clicking a link) using safe methods. According to MDN's cookie guide this is the default when the attribute is missing;None: sends it with every request, but needsSecure.SameSiteprotects against CSRF (Cross-Site Request Forgery), which chapter 8 discusses: an attacker's page starts a request toward Kurzuspont, and the browser would attach the cookie by itself.StrictandLaxprevent this.A further protection is the name prefix: a cookie with the
__Host-prefix can be set only withSecure, the pathPath=/, and withoutDomain, so it is bound precisely to the given host. The server must check the name together with the prefix.Third-party cookies and privacy
A third-party cookie belongs to embedded content (for example an
iframeor an advertisement) that arrives from another site, and also allows cross-site tracking. According to MDN, browsers increasingly block them by default. The GDPR (General Data Protection Regulation) and the ePrivacy directive ask for transparency and consent for the use of cookies that are not necessary. Lesson 9.6 on privacy discusses this question in detail.Lifecycle and logout
A session cannot last forever. It can expire, the user can log out, the server can terminate it for security reasons. Logout must therefore also invalidate the server-side access; it is not enough to show the interface as "not logged in". On the trial server, after logout a request sent with the same identifier is rejected:
Max-Age=0deletes the cookie on the browser side. What is really important, however, is the second request: the server no longer knows the identifier, so a copy of the old cookie cannot be used either. If logout only "cleaned" the browser, work could continue with an identifier obtained earlier.MDN gives two further pieces of advice for sessions: the session identifier must be regenerated on login or on entry (against the session fixation attack), and the lifetime of sensitive cookies should be short.
Worked example: the login cookie of Kurzuspont
__Host-sidSecure,Path=/.HttpOnlySecureSameSiteLaxMax-Age(The trial server is simplified, and runs without
Secureand the__Host-name prefix, because the example runs over HTTP onlocalhost.)Common misconceptions
HttpOnlyprotects against everything."In brief
HttpOnly,Secure,SameSite, short-lived, with a random identifier.Review questions
HttpOnly,Secure, andSameSitemean?Learning resources