Cookies are defined

We know that HTTP is stateless, which means that the server has no way of knowing whether the user is logged in or not when they do something else.

Clever little partner quickly thought, the user login information stored behind the interface can not be brought? However, there is no local storage method on the browser before cookies appear, and the strong variables in JS can not survive a refresh of the browser. In this wayThe appearance.

An HTTP Cookie (also known as a Web Cookie or browser Cookie) is a small piece of data that a server sends to a user’s browser and keeps locally. It is carried and sent to the server the next time the browser makes a request to the same server. Typically, it is used to keep the user logged in. Cookies make it possible to record stable state information over stateless HTTP protocols.

Cookies are mainly used for the following three aspects:

  • Session state management (such as user login status, shopping cart, game score, or other information that needs to be logged)
  • Personalization (such as user-defined Settings, themes, etc.)
  • Browser behavior tracking (e.g. tracking and analyzing user behavior, etc.)

Cookies are by nature bound to a specific domain name. When a cookie is set, it is included in any request to the domain that created it. This restriction ensures that the information contained in the cookie is accessible only to approved recipients. It cannot be accessed by another domain.

Cookie attribute

Ctx.cookie. set(name, value, [options]) Set the value of cookie name using options:

  • MaxAge A number representing the number of milliseconds from date.now ()
  • Signed cookie Signature Value Expires Indicates the Date when the cookie expires
  • Path Cookie path. The default value is’/’ domain cookie Domain name Secure Secure cookie
  • HttpOnly restricts cookies from being accessed by JavaScript scripts
  • Overwrite A Boolean value indicating whether to overwrite a previously set of the same name
  • Secure sends cookies only for Secure HTTPS communication

CSRF of actual combat

CSRF often uses the example of a bank whose url is localhost:8000, where a user logs in and accidentally opens a phishing site and is induced to make a transfer.

I’m not convinced that phishing sites can carry cookies from bank addresses, so the experiment is inevitable.

├ ─ ─ bad – end / / phishing site ├ ─ ─ ─ ─ app. Js / / server ├ ─ ─ ─ ─ public folder / / static ├ ─ ─ ─ ─ ─ ─ index. The HTML ├ ─ ─ front / / bank website ├ ─ ─ ─ ─ app. Js / / server ├ ─ ─ ─ ─ public folder / / static ├ ─ ─ ─ ─ ─ ─ index. The HTML / / homepage ├ ─ ─ ─ ─ ─ ─ the login. The HTML / / login page

  1. Create banking service (front/app.js)
const Koa = require('koa');
const route = require("koa-route")
const fs = require("fs");
const path = require("path");
const bodyParser = require('koa-bodyparser');
const app = new Koa();
// Store user information
let users = [{
  name: "xiaoMing".password: "123456".money: 10000}, {name: "badMan".password: "123456".money: 0
}]

let sessions = {}

app.use(bodyParser())

// Transfer interface
app.use(route.post('/api/transfer', ctx => {
  let body = ctx.request.body;
  let fromName = JSON.parse(ctx.cookies.get("key")).name;
  if(! sessions[fromName])return;
  let toUser = users.find((item) = > {
    return item.name === body.toName;
  })
  let fromUser = users.find((item) = > {
    return item.name === fromName;
  })

  if(toUser && fromUser && body.price){
    let price = Number(body.price)
    toUser.money +=  price;
    fromUser.money -= price;
    console.log(JSON.stringify(users))
  }
}));

// Static file service
app.use(route.get('/public/*', ctx => {
  ctx.body = getPublicFile(`.${ctx.request.url}`)}));// Login interface
app.use(route.post('/api/login', ctx => {
  let body = ctx.request.body;
  let find = users.find((item) = > {
    return item.name === body.name && item.password === body.password;
  })
  if(find){
    / / set the session
    sessions[body.name] = body.name;
    / / set the cookie
    ctx.cookies.set("key".JSON.stringify(find) )
    ctx.body = getPublicFile(`./public/index.html`)}}));function getPublicFile(url){
  return fs.readFileSync( path.resolve(__dirname, url),{encoding: 'utf8'}); } app.listen(8000);
Copy the code
  1. Create login page (front/public/login.html)

      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <h1>Hello Cookie!</h1>
    <form action="/api/login" method="post">
        <div>
            <input type="text" name="name"/>
        </div>
        <div>
            <input type="text" name="password"/>
        </div>
        <input type="submit" value="Submit"/>
    </form>
</body>
</html>
Copy the code
  1. Create a simple front page (front/public/index.html)

      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <h1>Bank</h1>
</body>
</html>
Copy the code
  1. Enter the page addresshttp://localhost:8000/public/login.html, fill in the user name password and submit
  2. Login succeeds and jump to the home page to get the cookie written by the server
  3. Create a phishing site server (bad-end/app.js)
const Koa = require('koa');
const route = require("koa-route")
const fs = require("fs");
const path = require("path");
const app = new Koa();

app.use(bodyParser())
app.use(route.get('/public/*', ctx => {
  ctx.body = getPublicFile(`.${ctx.request.url}`)}));function getPublicFile(url){
  return fs.readFileSync( path.resolve(__dirname, url),{encoding: 'utf8'}); } app.listen(3000);
Copy the code
  1. Create a phishing page (bad-end/public/index.html)

      
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>Document</title>
</head>
<body>
    <form action="http://localhost:8000/api/transfer" method="post">
        <div style="display: none;">
            <input type="text" name="toName" value="badMan" id="">
            <input type="text" name="price" value="1000" id="">    
        </div>
        <input type="submit" value="点我">
    </form>
</body>
</html>
Copy the code
  1. When we’re in the same browser, open localhost:3000

  1. The domain field of a Cookie on the browser side is equal to aaA. www.com or www.com
  2. Both are HTTP or HTTPS, or the Secure property is false in different cases
  3. To send the request path, that is, the above XXXXX must be the same as the path property of the browser-side Cookie, or the subdirectory of the path of the browser-side Cookie, for example, the path of the browser-side Cookie is /test. XXXXXXX must be a /test or /test/ XXXX subdirectory

The preceding three conditions must be met; otherwise, the Post request will not automatically carry an existing Cookie on the browser

Cookie-based CSRF defense

1. Verify the HTTP Referer field

The HTTP request header referer field filters out illegal Cookie carriers through the referer accessible whitelist. But this method is not foolproof, and some browsers have bugs in the Referer that have been modified.

2. SameSite properties

cookiesProperty is used to limit third-party cookies, thereby reducing security risks.

It can set three values:

  1. Strict does not send cookies under any circumstances when it crosses sites
  2. Lax also does not send third-party cookies in most cases, except for Get requests that navigate to the target url
  3. The None site has the option of explicitly turning off the SameSite property and setting it to None. However, the Secure attribute must be set at the same time (cookies can only be sent through HTTPS). Otherwise, cookies are invalid.
SameSite=None; Secure
SameSite=Strict
Copy the code

Other CSRF defenses

1. Use the token on the browser

throughGenerate tokens, store tokens using stroage, and carry them into subsequent request headers. Realization of state recognition.

reference

  • The SameSite property of the Cookie
  • MDN