preface
I am a CRUD only page, before, I was always confused about the attack means such as XSS, CSRF attack, there is no intuitive concept, after reading the article only vaguely understand, until it is linked with cookie, and then practice again, it is really understood. It also gives you a better understanding of why cookies have those fields. Today, I want to share these with you, I believe it will help you too ~
About the basic knowledge of Cookie, MDN is more detailed, I will not repeat, this article focuses on the actual combat above, so that you can have a more intuitive, profound understanding.
This is the first section, the content is relatively basic, simple, but this is to pave the way for the next chapter, the next chapter will explain the content of security and prevention on the basis of today.
Preliminary knowledge
In order to help you read more smoothly, I will help you to briefly review or introduce some pre-knowledge.
cookie
Cookie is a piece of data sent by the server to the client and saved by the client (browser). The server is set with the set-cookie response header, and each subsequent request from the client automatically carries the cookie field.
The set-cookie in the response header can only carry one name-value pair in the name/value format at a time, so you may see multiple set-cookie fields in the response. Cookies can have multiple name-value pairs, so there is only one in the request.
As for set-cookie, what is the specific format of cookie and how they work, I will explain them in the following content combined with examples.
Node.js
You can use readFileSync to read files and writeFileSync to write files.
Express.js
We’re just going to use a little bit of it, the Get Started section on the website, the use of middleware.
The basic use
For its scalability and scalability, HTTP is designed as a stateless protocol. However, it is necessary for almost every website to record user states. In this case, HTTP needs an HTTP State Management Mechanism. So cookie comes out. So how does a cookie record a user’s state? We will demonstrate this through a practical case.
In this section, I’ll use writing a login interface as an example to see how cookies can be used to record user state.
1. Initialize the project
mkdir cookie-playground
cd cookie-playground
yarn init -y
yarn add express
touch app.js
mkdir public
touch public/index.html public/main.js
Copy the code
When you’re done, the directory looks like this:
Public directory is the resource directory of the front end, and app.js is the main entry file of the back end.
2. Fill in the basic page structure
The next step is to fill each file with the following:
// filename: app.js
const express = require('express')
const app = express()
const port = 3000
app.use(express.static('public'))
app.listen(port, () = > {
console.log('Please open your browser at http://localhost:3000)})Copy the code
<! -- filename: public/index.html -->
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<h1>hello<span id="userName">_</span></h1>
<script src="./mian.js"></script>
</body>
</html>
Copy the code
// filename: public/main.js
document.querySelector('#userName').textContent = 'Username'
Copy the code
If all of the above is correct, running Node app.js from the command line in the current directory will successfully start a server on port 3000 of the machine, and when we visit http://localhost:3000 we will get the page shown below.
3. Write the login interface
Create a new login page in the public directory:
touch public/login.html public/login.js
Copy the code
Fill in the following:
<! -- filename: public/login.html -->
<! DOCTYPEhtml>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="Width = device - width, initial - scale = 1.0">
<title>Document</title>
</head>
<body>
<form id="loginForm">
<input id="name" type="text">
<input id="password" type="password">
<button>The login</button>
</form>
<script src="./login.js"></script>
</body>
</html>
Copy the code
// filename: login.js
document.querySelector('#loginForm').addEventListener('submit'.function (e) {
e.preventDefault();
const name = document.querySelector('#name').value
const password = document.querySelector('#password').value
console.log(name, password)
})
Copy the code
The effect is as follows:
For convenience, I will not write the login interface and the registration interface separately, but mix them into one interface, if the current user does not exist, to help him to register. Login is not allowed if and only if the user name exists but the password is incorrect. If the user logs in successfully, we jump back to the index.html page.
In order to remember whether the user has registered and keep the user’s login information, we need to introduce a database. For convenience, I only use a local file as our database
touch db.txt
Copy the code
The content format is similar to:
username1=123
username2=456
...
Copy the code
Now that we are ready, let’s write the login interface:
app.use(express.json());
app.post('/login'.(req, res) = > {
const { name, password } = req.body;
// All the code at the end of this article has the implementation of this function, there is no space
Record
,>
const users = readFromDB();
if(users[name] && password ! == users[name]) { res.send({success: false.message: 'Incorrect password' });
return;
}
// The user name does not exist
if(! users[name]) { users[name] = password; }// All the code at the end of this article has the implementation of this function, there is no space
// Synchronize user information to the database
syncUsersToDB(users)
res.redirect('/')})Copy the code
On the front end, we click the login button, and if the new or old user enters the correct password, it jumps to the index.html page, which is:
document.querySelector('#loginForm').addEventListener('submit'.function (e) {
e.preventDefault();
const name = document.querySelector('#name').value
const password = document.querySelector('#password').value
fetch('/login', {
method: 'POST'.headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ name, password })
}).then(res= > {
if (res.redirected) {
window.location.href = res.url;
} else {
res.text().then(err= >{ alert(err); }); }})})Copy the code
This way the login interface is written.
4. Use cookies to save login status
The user must visit our home page at the beginning, at this time we need to judge whether the user has logged in, if not, jump to the login page, if already logged in, show the user name on the page.
The point is, how do you keep track of whether the user is logged in? So we’re going to use cookies here. There’s nothing going on at the front end, it’s all going on at the back end using set-cookie.
So what’s the format of set-cookie?
When we visit www.baidu.com, we can see the set-cookie field in the response header as shown in the following figure.
It’s a weird format. No hurry. Come clean with me.
Take the Set – cookies: BD_HOME = 1; Path = /, for example.
The format of the set-cookie field is cookie-name=cookie-value. In the preceding paragraph, BD_HOME is cookie-name and 1 is cookie-value. You can also check the value by using the browser:
Notice that in the figure above, there are also items after value, such as Domain, Path, Expires. How do you control these Settings? These are also Set in set-cookie, following cookie-name=cookie-value. Space. Path = / is. In a set-cookie, there can be multiple name-value pairs that control the Cookie. We will deal with them later in the security section.
In this section, we just need to set the value of the cookie to remember the state, and don’t worry about anything else for now.
Add the following code to the login interface:
const crypto = require('crypto')
const NodeCache = require("node-cache");
// Set the cookie to expire in 10 seconds for easy emulation
const cookieCache = new NodeCache({ stdTTL: 10 });
// omit unwanted code....
app.post('/login'.(req, res) = > {
// omit unwanted code....
const uuid = crypto.randomUUID();
if(! cookieCache.has(name)) { cookieCache.set(uuid, name); } res.cookie('sessionid', uuid)
// omit unwanted code....
res.redirect('/')})Copy the code
According to whether there is a login and access to the main page to do the difference jump:
const cookieParser = require("cookie-parser");
app.use(cookieParser());
app.get('/'.function (req, res) {
const cookies = req.cookies;
const hasLogin = cookieCache.has(cookies.sessionid);
if (hasLogin) {
return res.redirect('/index.html');
} else {
return res.redirect('/login.html')}});Copy the code
At this point, check the NetWork panel to see the sessionID set:
We also left a space on the page called the username.
We’ll write a getUserInfo interface that will be accessed when we get to the home page. If we’re logged in, we’ll get the current user name, otherwise we’ll redirect to the login page.
app.get('/getUserInfo'.function (req, res) {
const cookies = req.cookies;
const hasLogin = cookieCache.has(cookies.sessionid);
if (hasLogin) {
res.send({
name: cookieCache.get(cookies.sessionid)
})
} else {
return res.redirect('/login.html')}})Copy the code
The place to use it is also relatively simple:
fetch('/getUserInfo', {
method: 'GET',
}).then(res= > {
if(! res.redirected) { res.text().then(r= > {
document.querySelector('#userName').textContent = JSON.parse(r).name;
});
} else {
window.location.href = res.url
}
})
Copy the code
The effect after successful login is as follows:
When the cookie expires, it is redirected to the login.html page.
To draw a flow chart of the whole process, it would be:
These are the basic uses. In the next chapter, security and prevention will be explained on this basis, including man-in-the-middle attack, cookie hijacking, XSS attack, CSFR attack and so on.