7
Author: valentinog
Translator: Front-end Xiaozhi Source: valentinog
If you have dreams and dry goods, you can search for [Great Relocation to the World] on WeChat and pay attention to this Shuwanzhi who is still washing dishes in the early hours of the morning.
This article GitHub https://github.com/qq449245884/xiaozhi has been included, there are complete test sites, materials and my series of articles for interviews with first-line manufacturers.

What are cookies in web development?

A cookie is a small piece of data that the backend can store in the user's browser. The most common use cases for cookies include user tracking, personalization, and authentication.

Cookies have many privacy concerns and have been heavily regulated over the years.

In this article, focus on the technical aspects: learning how to create, use HTTP cookies on the front-end and back-end.

Backend configuration

The backend example is written in Flask . If you want to follow along, you can create a new Python virtual environment, move into it and install Flask

 mkdir cookies && cd $_

python3 -m venv venv
source venv/bin/activate

pip install Flask

Create a new file named 为flask app.py in the project folder and experiment locally using the examples in this article.

Who creates cookies?

First, where do cookies come from? who creates cookies?

While it is possible to use document.cookie to create a cookie in the browser, in most cases it is the backend's responsibility to set the cookie in the request before it will respond to the client request.

Backend means that cookies can be created in the following ways:

  • Code for the actual application on the backend (Python, JavaScript, PHP, Java)
  • A web server (Nginx, Apache) that responds to requests

Backends can set cookies in HTTP requests with the Set-Cookie attribute, which is a corresponding string consisting of key/value pairs and optional attributes:

 Set-Cookie: myfirstcookie=somecookievalue

When do you need to create a cookie? It depends on demand.

Cookies are simple strings. Create a Python file named flask_app.py in the project folder and enter the following:

 from flask import Flask, make_response

app = Flask(__name__)


@app.route("/index/", methods=["GET"])
def index():
    response = make_response("Here, take some cookie!")
    response.headers["Set-Cookie"] = "myfirstcookie=somecookievalue"
    return response

Then run the application:

 FLASK_ENV=development FLASK_APP=flask_app.py flask run

When the app runs, the user accesses http://127.0.0.1:5000/index/ and the backend will set a response header named Set-Cookie with a key/value pair.

( 127.0.0.1:5000 is the default listening address/port for developing Flask applications).

Set-Cookie header is the key to understanding how cookies are created:

 response.headers["Set-Cookie"] = "myfirstcookie=somecookievalue"

Most frameworks have their own way of setting cookies, such as Flask's set_cookie() .

How to view cookies?

After visiting http://127.0.0.1:5000/index/ , the backend will set a cookie in the browser. To view this cookie, call document.cookie from the browser's console:

clipboard.png

Or you can check the Storage tab in the developer tools. Click on the cookie, you will see the specific content of the cookie:

clipboard.png

On the command line, you can also use curl to see which cookies are set by the backend

 curl -I http://127.0.0.1:5000/index/

Cookies can be saved to a file for later use:

 curl -I http://127.0.0.1:5000/index/ --cookie-jar mycookies

Display cookies on stdout:

 curl -I http://127.0.0.1:5000/index/ --cookie-jar -

Please note that there is no HttpOnly attribute of cookie , in the browser you can use the document.cookie HttpOnly attribute to access, if set ---5dc519be66ecc32cec59be665-cc32cec59be65 document.cookie cc32aa document.cookie can't read it.

 Set-Cookie: myfirstcookie=somecookievalue; HttpOnly

Now, the cookie will still appear in the Storage tab, but document.cookie return an empty string.

From now on, cookies are created on the backend using Flask's response.set_cookie() for convenience.

I have a cookie, what do I do now?

Your browser gets a cookie. Now what? Once you have the cookie, the browser can send the cookie back to the backend.

This has many uses such as: user tracking, personalization, and most importantly, authentication.

For example, once you log into the site, the backend will give you a cookie:

 Set-Cookie: userid=sup3r4n0m-us3r-1d3nt1f13r

In order to correctly identify us in each subsequent request, the backend checks the cookie from the browser in the request

To send a cookie, the browser appends a Cookie header to the request:

 Cookie: userid=sup3r4n0m-us3r-1d3nt1f13r

Cookies can be set to expire: Max-Age and expires

By default, cookies expire when the user closes the session, that is, when the browser is closed. To persist cookies, we can pass the expires or Max-Age attribute

 Set-Cookie: myfirstcookie=somecookievalue; expires=Tue, 09 Jun 2020 15:46:52 GMT; Max-Age=1209600

Note: Max-Age takes precedence over expires .

The scope of the cookie is the website path: the path attribute

Consider this backend, which sets a new cookie for its frontend when visiting http://127.0.0.1:5000/ . Instead, on the other two paths, we print the requested cookie :

 from flask import Flask, make_response, request

app = Flask(__name__)


@app.route("/", methods=["GET"])
def index():
    response = make_response("Here, take some cookie!")
    response.set_cookie(key="id", value="3db4adj3d", path="/about/")
    return response


@app.route("/about/", methods=["GET"])
def about():
    print(request.cookies)
    return "Hello world!"


@app.route("/contact/", methods=["GET"])
def contact():
    print(request.cookies)
    return "Hello world!"

Run the application:

 FLASK_ENV=development FLASK_APP=flask_app.py flask run

In another terminal, if we make a connection to the root route, we can see the cookie in 在Set-Cookie :

 curl -I http://127.0.0.1:5000/ --cookie-jar cookies

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 23
Set-Cookie: id=3db4adj3d; Path=/about/
Server: Werkzeug/1.0.1 Python/3.8.3
Date: Wed, 27 May 2020 09:21:37 GMT

Note that at this point the cookie has Path attribute:

 Set-Cookie: id=3db4adj3d; Path=/about/

/about/ Route and save cookies

 curl -I http://127.0.0.1:5000/about/ --cookie cookies

Run the following command in the terminal of the Flask application, you can see:

 ImmutableMultiDict([('id', '3db4adj3d')])
127.0.0.1 - - [27/May/2020 11:27:55] "HEAD /about/ HTTP/1.1" 200 -

As expected, the cookie is returned to the backend. Now try to access /contact/ route:

 url -I http://127.0.0.1:5000/contact/ --cookie cookies

Run the following command in the terminal of the Flask application, you can see:

 ImmutableMultiDict([])
127.0.0.1 - - [27/May/2020 11:29:00] "HEAD /contact/ HTTP/1.1" 200 -

What does this mean? The scope of the cookie is Path . A cookie with a given path attribute cannot be sent to another unrelated path, even if the two paths are in the same domain.

This is the first layer of cookie permissions.

When Path is omitted during cookie creation, the browser defaults to / .

The scope of the cookie is the domain name: the domain attribute

The value of the cookie's Domain attribute controls whether the browser should accept cookies and where the cookie is returned.

Let's look at some examples.

Everyone said that there is no project to write on the resume, so I helped you find a project, and also included a [Building Tutorial] .

host mismatch (wrong host)

View the cookies set by https://serene-bastion-01422.herokuapp.com/get-wrong-domain-cookie/ :

 Set-Cookie: coookiename=wr0ng-d0m41n-c00k13; Domain=api.valentinog.com

Here the cookie is from serene-bastion-01422.herokuapp.com , but the Domain attribute has api.valentinog.com .

Browsers have no other option to refuse this cookie. For example Chrome will give a warning (Firefox does not)

clipboard.png

Host mismatch (subdomain)

View the cookies set by https://serene-bastion-01422.herokuapp.com/get-wrong-subdomain-cookie/ :

 Set-Cookie: coookiename=wr0ng-subd0m41n-c00k13; Domain=secure-brushlands-44802.herokuapp.com

The cookie here is from serene-bastion-01422.herokuapp.com , but the "Domain" attribute is secure-brushlands-44802.herokuapp.com .

They are on the same domain, but the subdomains are different. Likewise, browsers also reject this cookie:

clipboard.png

host match (entire domain)

View the cookies set by https://www.valentinog.com/get-domain-cookie.html :

 set-cookie: cookiename=d0m41n-c00k13; Domain=valentinog.com

This cookie is set on the web server using Nginx add_header:

 add_header Set-Cookie "cookiename=d0m41n-c00k13; Domain=valentinog.com";

There are various ways of setting cookies in Nginx used here. Cookies are set by the code of the web server or application and are irrelevant to the browser.

What matters is which domain the cookie comes from.

The browser will happily accept cookies here, because the host in Domain includes the host the cookie came from.

In other words, valentinog.com includes the subdomain www.valentinog.com .

At the same time, new requests for 对valentinog.com will carry the cookie, as well as any requests to the valentinog.com subdomain.

Here's a www subdomain request with a cookie attached:

clipboard.png

Below is a request to another subdomain that automatically appends cookies

clipboard.png

List of Cookies and Public Suffixes

View the cookies set by https://serene-bastion-01422.herokuapp.com/get-domain-cookie/: :

 Set-Cookie: coookiename=d0m41n-c00k13; Domain=herokuapp.com

Here the cookie from serene-bas-01422.herokuapp.com , Domain attribute is herokuapp.com . what the browser should do here

You might think that serene-base-01422.herokuapp.com is contained in the herokuapp.com domain, so the browser should accept cookies.

Instead, it rejects the cookie because it is from a domain included in the public suffix list .

Public Suffix List. This list lists top-level domains and domains that are open for registration. Browsers prevent domains on this list from being written to cookies by subdomains.

host match (subdomain)

View the cookies set by https://serene-bastion-01422.herokuapp.com/get-subdomain-cookie/: :

 Set-Cookie: coookiename=subd0m41n-c00k13

When the domain is omitted during cookie creation, the browser defaults to showing the origin host in the address bar, in this case my code does this:

 response.set_cookie(key="coookiename", value="subd0m41n-c00k13")

When the cookie goes into the browser's cookie store, we see that Domain is applied:

clipboard.png

Now, we have the cookie from serene-bastion-01422.herokuapp.com , where should the cookie go now?

If you visit https://serene-bastion-01422.herokuapp.com/ , the cookie comes with the request:

clipboard.png

However, if you visit herokuapp.com , the cookie does not appear with the request:

clipboard.png

In a nutshell, browsers use the following heuristics to decide what to do with cookies (where sender host refers to the actual URL you visit):

  • Completely reject cookies if the domain or subdomain in "Domain" does not match the visited host
  • Reject the cookie if the value of Domain is included in the public suffix list
  • If the domain or subdomain in Domain matches the visited host, accept the cookie

Once the browser accepts the cookie, and is about to make a request, it says:

  • If the requesting host exactly matches the value I see in Domain the cookie will just be returned
  • If the requesting host is a subdomain that exactly matches the value I see in "Domain", the cookie will be passed back
  • If the requesting host is a subdomain like sub.example.dev example.dev , a cookie will be returned
  • If the requesting host is a primary domain such as example.dev and the Domain is such as sub.example.dev , no cookie will be returned.

The Domain and Path properties have always been the second layer of cookie permissions.

Cookies can be passed through AJAX requests

Cookies can be propagated through AJAX requests. AJAX requests are asynchronous HTTP requests made using JS (XMLHttpRequest or Fetch) to fetch data and send it back to the backend.

Consider another example from Flask with a template that in turn loads a JS file:

 from flask import Flask, make_response, render_template

app = Flask(__name__)


@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")


@app.route("/get-cookie/", methods=["GET"])
def get_cookie():
    response = make_response("Here, take some cookie!")
    response.set_cookie(key="id", value="3db4adj3d")
    return response

Here is the templates/index.html template:

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button>FETCH</button>
</body>
<script src="{{ url_for('static', filename='index.js') }}"></script>
</html>

Here is the content of static/index.js :

 const button = document.getElementsByTagName("button")[0];

button.addEventListener("click", function() {
  getACookie();
});

function getACookie() {
  fetch("/get-cookie/")
    .then(response => {
      // make sure to check response.ok in the real world!
      return response.text();
    })
    .then(text => console.log(text));
}

When visiting http://127.0.0.1:5000/ we see a button. By clicking the button, we make a get request to /get-cookie/ and get the cookie. As expected, the cookie lands in the browser's cookie storage.

Make some changes to the Flask app, adding one more route:

 from flask import Flask, make_response, request, render_template, jsonify

app = Flask(__name__)


@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")


@app.route("/get-cookie/", methods=["GET"])
def get_cookie():
    response = make_response("Here, take some cookie!")
    response.set_cookie(key="id", value="3db4adj3d")
    return response


@app.route("/api/cities/", methods=["GET"])
def cities():
    if request.cookies["id"] == "3db4adj3d":
        cities = [{"name": "Rome", "id": 1}, {"name": "Siena", "id": 2}]
        return jsonify(cities)
    return jsonify(msg="Ops!")

In addition, adjust the JS code to request the newly added route:

 const button = document.getElementsByTagName("button")[0];

button.addEventListener("click", function() {
  getACookie().then(() => getData());
});

function getACookie() {
  return fetch("/get-cookie/").then(response => {
    // make sure to check response.ok in the real world!
    return Promise.resolve("All good, fetch the data");
  });
}

function getData() {
  fetch("/api/cities/")
    .then(response => {
      // make sure to check response.ok in the real world!
      return response.json();
    })
    .then(json => console.log(json));

When visiting http://127.0.0.1:5000/ we see a button. By clicking the button, we make a get request to /get-cookie/ to get the cookie. After the cookie appears, we will make another Fetch request to /api/cities/ .

In the browser's console, you can see the data returned by the request. In addition, in the Network tab of the developer tools, you can see a header called Cookie, which is passed to the backend through an AJAX request.

Exchanging cookies back and forth between frontend and backend works fine as long as the frontend is in the same context as the backend: we say they come from the same origin.

This is because by default, Fetch only sends credentials when the request arrives at the origin that triggered the request, i.e. Cookie .

Cookies cannot always be passed through AJAX requests

Consider another case, running independently on the backend, the application can be started like this:

 FLASK_ENV=development FLASK_APP=flask_app.py flask run

Now, in a different folder than the Flask app, create index.html :

 <!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<button>FETCH</button>
</body>
<script src="index.js"></script>
</html>

Create a JS file named index.js in the same folder with the following code:

 button.addEventListener("click", function() {
  getACookie().then(() => getData());
});

function getACookie() {
  return fetch("http://localhost:5000/get-cookie/").then(response => {
    // make sure to check response.ok in the real world!
    return Promise.resolve("All good, fetch the data");
  });
}

function getData() {
  fetch("http://localhost:5000/api/cities/")
    .then(response => {
      // make sure to check response.ok in the real world!
      return response.json();
    })
    .then(json => console.log(json));
}

In the same folder, from a terminal run:

 npx serve

This command gives you the local 地址/端口 to connect to, for example http://localhost:42091/ . Visit the page and try to click the button with the browser console open. In the console, you can see:

 Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/get-cookie/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)

Because http://localhost:5000/ --- is different from http://localhost:42091/. . They are different domains, so there will be a limitation of CORS .

Everyone said that there is no project to write on the resume, so I helped you find a project, and also included a [Building Tutorial] .

Handling CORS

CORS is a W3C standard whose full name is "Cross-origin resource sharing". It allows browsers to issue XMLHttpRequest requests to cross-domain servers, thus overcoming the limitation that AJAX can only be used with the same origin.

The entire CORS communication process is completed automatically by the browser and does not require user participation. For developers, CORS communication is no different from ordinary AJAX communication, and the code is exactly the same. Once the browser finds that the AJAX request is cross-domain, it will automatically add some additional header information, and sometimes an additional request will be made, but the user will not be aware of it. Therefore, the key to implementing CORS communication is the server. Cross-domain communication is possible as long as the server implements the CORS interface.

By default, browsers will block AJAX requests to remote resources that are not of the same origin unless the server sets a specific HTTP header of Access-Control-Allow-Origin .

To fix this first error, we need to configure CORS for Flask:

 pip install flask-cors

Then apply CORS to Flask:

 from flask import Flask, make_response, request, render_template, jsonify
from flask_cors import CORS

app = Flask(__name__)
CORS(app=app)


@app.route("/", methods=["GET"])
def index():
    return render_template("index.html")


@app.route("/get-cookie/", methods=["GET"])
def get_cookie():
    response = make_response("Here, take some cookie!")
    response.set_cookie(key="id", value="3db4adj3d")
    return response


@app.route("/api/cities/", methods=["GET"])
def cities():
    if request.cookies["id"] == "3db4adj3d":
        cities = [{"name": "Rome", "id": 1}, {"name": "Siena", "id": 2}]
        return jsonify(cities)
    return jsonify(msg="Ops!")

Now try clicking the button again with the browser console open. In the console you should see

 Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/api/cities/. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing)

Although we made the same mistake, the culprit this time was the second route.

You can confirm by looking at the request in the "Network" tab that no such cookies are being sent:

clipboard.png

In order to include cookies in Fetch requests from different origins, we have to mention the credentials flag (by default, it is the same origin).

Without this flag, Fetch ignores cookies, which can be fixed like this:

 const button = document.getElementsByTagName("button")[0];

button.addEventListener("click", function() {
  getACookie().then(() => getData());
});

function getACookie() {
  return fetch("http://localhost:5000/get-cookie/", {
    credentials: "include"
  }).then(response => {
    // make sure to check response.ok in the real world!
    return Promise.resolve("All good, fetch the data");
  });
}

function getData() {
  fetch("http://localhost:5000/api/cities/", {
    credentials: "include"
  })
    .then(response => {
      // make sure to check response.ok in the real world!
      return response.json();
    })
    .then(json => console.log(json));
}

credentials: "include" must be present in the first Fetch request to save the cookie in the browser's cookie storage:

 fetch("http://localhost:5000/get-cookie/", {
    credentials: "include"
  })

It must also be present on the second request to allow the cookie to be transferred back to the backend

 fetch("http://localhost:5000/api/cities/", {
    credentials: "include"
  })

Try again, we also need to fix another bug in the backend:

 Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:5000/get-cookie/. (Reason: expected ‘true’ in CORS header ‘Access-Control-Allow-Credentials’).

To allow cookies to be transmitted in CORS requests, the backend also needs to set the Access-Control-Allow-Credentials header.

 CORS(app=app, supports_credentials=True)

Important: In order for cookies to be passed through AJAX requests between different origins, this can be done:

  • credentials: "include" is used in front-end fetch requests
  • Access-Control-Allow-Credentials and Access-Control-Allow-Origin for the backend

Cookies can be passed through AJAX requests, but they must obey the domain rules we described earlier.

Cookie's Secure attribute

The Secure attribute means that if a cookie is set Secure=true , then the cookie can only be sent to the server using the https protocol, but not using the http protocol. In other words, the cookie is created under the condition of https , and its Secure=true, then you have been using https to visit other pages (such as clicking on other sub-pages after logging in), the cookie will be Sent to the server, you can jump to other pages without logging in again. But if you change the url to http protocol to access other pages, you need to log in again, because this cookie cannot be sent in http protocol.

The Secure property can be set like this

 response.set_cookie(key="id", value="3db4adj3d", secure=True)

If you want to try it in a real environment, you can run the following command, and note that curl is not passed here HTTP save the cookie:

 curl -I http://serene-bastion-01422.herokuapp.com/get-secure-cookie/ --cookie-jar -

Instead, over HTTPS, the cookie appears in cookie jar :

 curl -I https://serene-bastion-01422.herokuapp.com/get-secure-cookie/ --cookie-jar -

cookie jar file:

 serene-bastion-01422.herokuapp.com      FALSE   /       TRUE    0

Don't be fooled by Secure : the browser accepts cookies via HTTPS , but once the cookie enters the browser, there is no protection.

Because cookies with Secure are generally not used to transmit sensitive data.

HttpOnly attribute of cookies

If the HttpOnly attribute is set in the cookie, the cookie information cannot be read through the js script, which can effectively prevent XSS attacks and steal the cookie content, thus increasing the security of the cookie. Even so, do not put important information Store cookies.

The full name of XSS is Cross SiteScript, cross-site scripting attack, which is a common vulnerability in Web programs. XSS is a passive and client-side attack method, so its harmfulness is easily ignored. The principle is that an attacker enters (passes in) malicious HTML code into a website with XSS vulnerabilities. When other users browse the website, this HTML code will be automatically executed, thereby achieving the purpose of the attack. For example, stealing user cookies, destroying page structure, redirecting to other websites, etc.

If you have set HttpOnly it looks like this:

 Set-Cookie: "id=3db4adj3d; HttpOnly"

in Flask

 response.set_cookie(key="id", value="3db4adj3d", httponly=True)

In this way, the cookie is set with the HttpOnly attribute, then the cookie information cannot be read through the js script. If checked in the console, document.cookie will return an empty string.

When to use HttpOnly ? Cookies should always be HttpOnly unless there is a specific requirement to expose them to runtime JS.

Terrible SameSite Properties

first-party cookies and third-party cookies

View the cookies carried in https://serene-bastion-01422.herokuapp.com/get-cookie/

 Set-Cookie: simplecookiename=c00l-c00k13; Path=/

first-party refers to the cookie issued by the website you log in or use, while third-party cookie is often some advertising websites, which may violate privacy and security risks.

We call this type of cookie first-party . That is, I visit the URL in the browser, and if I visit the same URL or another path to the site (let's say the Path is / ), the browser sends the cookie back to the site .

Now consider another web page at https://serene-bastion-01422.herokuapp.com/get-frog/ . The page sets a cookie, and in addition, it loads images from a remote resource hosted by 从https://www.valentinog.com/cookie-frog.jpg .

The remote resource will in turn set a cookie by itself:

clipboard.png

We refer to such cookies as third-party (third party) cookies.

In addition to being used for CSRF attacks, third-party cookies can also be used for user tracking. For example, Facebook inserts an invisible image on a third-party website.

 ![](facebook.com)

When the browser loads the above code, it will send a request to Facebook with a cookie, so that Facebook will know who you are and what website you are visiting.

Using the SameSite property

The SameSite attribute of the cookie is used to restrict the third-party cookie, thereby reducing security risks. It can set three values.

  • Strict
  • Lax
  • None

Strict is the most strict, completely prohibits third-party cookies, and will not send cookies under any circumstances when crossing sites. In other words, only the URL of the current web page is consistent with the request target, the cookie will be brought.

 Set-Cookie: CookieName=CookieValue; SameSite=Strict;

This rule is too strict and may result in a very bad user experience. For example, if the current web page has a GitHub link, the user will not have GitHub's cookie when they click to jump, and the jump is always in the state of not logged in.

Lax rules are slightly relaxed, and third-party cookies are not sent in most cases, except for Get requests that navigate to the destination URL.

 Set-Cookie: CookieName=CookieValue; SameSite=Lax;

A GET request for navigating to a destination URL includes only three cases: a link, a preload request, and a GET form. See the table below for details.

clipboard.png

After setting Strict or Lax , CSRF attack is basically eliminated. Of course, the premise is that the user's browser supports the SameSite attribute.

Chrome plans to make Lax the default. At this time, the website can choose to explicitly close the SameSite attribute and set it to None. However, the premise is that the Secure attribute must be set at the same time (Cookies can only be sent through the HTTPS protocol), otherwise it will be invalid.

The following settings are invalid.

 Set-Cookie: widget_session=abc123; SameSite=None

The following settings are valid.

 Set-Cookie: widget_session=abc123; SameSite=None; Secure

Cookies and Authentication

Authentication is one of the most challenging tasks in web development. There seems to be a lot of confusion on this topic, as token-based authentication in JWT seems to be replacing "old", reliable modes like session-based authentication.

Let's see what role cookies play here.

session-based authentication

Authentication is one of the most common use cases for cookies.

When you visit a website that requests authentication, the backend will send a Set-Cookie header to the frontend in the background via credential submission (e.g. via a form).

A session cookie of type is as follows:

 Set-Cookie: sessionid=sty1z3kz11mpqxjv648mqwlx4ginpt6c; expires=Tue, 09 Jun 2020 15:46:52 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax

In this Set-Cookie header, the server can include a file named session , session id or similar cookie .

This is a unique identifier that is clearly visible to the browser. Whenever an authenticated user requests a new page from the backend, the browser sends back a session cookie .

Session-based authentication is stateful because the backend must keep track of each user's session. The storage of these sessions may be:

  • database
  • A key/value store like Redis
  • File system

Of the three session stores, something like Redis should take precedence over a database or filesystem.

Note that session-based authentication has nothing to do with the browser's session storage.

It is called a session- based session because the relevant data for user identification exists in the backend's session store, which is different from the browser's session store.

When to use session-based authentication

Use it whenever you can. Session-based authentication is the simplest, most secure, and direct form of website authentication. By default it is available on all popular web frameworks Django etc.

However, its stateful nature is also its main disadvantage, especially when the website is served by a load balancer. In this case, techniques like sticking sessions, or storing sessions on a centralized Redis store can help.

Everyone said that there is no project to write on the resume, so I helped you find a project, and also included a [Building Tutorial] .

A note on JWTs

JWT , short for JSON Web Tokens , is an authentication mechanism that has become increasingly popular in recent years.

JWT is great for single-page and mobile applications, but it introduces a new set of challenges. A typical flow for a front-end application that wants to authenticate against an API is as follows:

  • Frontend sends credentials to backend
  • The backend checks the credentials and sends back a token
  • The frontend carries the token on every subsequent request

The main question that comes with this approach is: where do I store this token in the front end in order to keep the user logged in?

The most natural thing for front-end development is to save the token in localStorage . This is bad for many reasons.

localStorage is easily accessible from JS code, and it's an easy target for XSS attacks.

To get around this, most developers keep the JWT token in cookie thinking that HttpOnly and Secure would protect the cookie, at least from XSS attacks.

Setting SameSite to strict completely protects the JWT from CSRF attacks

The new SameSite property set to SameSite = Strict will also protect your "cooked" JWT from CSRF attacks. However, since SameSite = Strict does not send cookies on cross-origin requests, this also completely invalidates the use case for JWT.

What about SameSite=Lax ? This mode allows cookies to be sent back using secure HTTP methods (i.e. GET, HEAD, OPTIONS and TRACE). POST requests do not transmit cookies in either way.

Actually, it is not a good idea to store the JWT tag in cookie or localStorage .

If you really want to use JWT instead of sticking to session-based authentication and extending the session store, you might want to use JWT with a refresh token to keep the user logged in.

Summarize

HTTP cookies have been around since 1994, they are everywhere.

Cookies are simple text strings, but their permissions can be controlled by Domain and Path . Cookies with Secure can only be transmitted over HTTPS, while HttpOnly can be used from JS is hidden.

However, for all intended uses, cookies can expose users to attacks and vulnerabilities.

Browser vendors and the Internet Engineering Task Force have worked year after year to improve cookie security, the most recent step being SameSite .

So, what constitutes a relatively secure cookie? , the following points:

  • Use HTTPS only
  • With the HttpOnly attribute as much as possible
  • Correct SameSite configuration
  • Does not carry sensitive data

Talents' [Three Links] is the biggest motivation for Xiaozhi to keep sharing. If there are any mistakes and suggestions in this blog, you are welcome to leave a message. Finally, thank you for watching.


The bugs that may exist after the code is deployed cannot be known in real time. In order to solve these bugs afterwards, a lot of time is spent on log debugging. By the way, I recommend a useful bug monitoring tool , Fundebug .

Original: https://gizmodo.com/the-complete-guide-to-cookies-and-all-the-stuff-w-1794247382

comminicate

If you have dreams and dry goods, you can search for [Great Move to the World] on WeChat and pay attention to this Shawanzhi who is still washing dishes in the early hours of the morning.

This article GitHub https://github.com/qq449245884/xiaozhi has been included, there are complete test sites, materials and my series of articles for interviews with first-line manufacturers.


王大冶
68.1k 声望105k 粉丝