In the previous article, we discussed the method of cookie+session to check the user status, but handling CSRF (cross-site request forgery) is a bit more troublesome.

Now that we’re talking about CSRF, let me explain a little bit.

CSRF

CSRF is a method of attack. In common parlance, an attacker impersonates you. How does this work? In principle, this kind of attack does not need to steal users’ cookies, but directly use users’ cookies to commit illegal acts.

How did you do that?

Let me simulate the situation:

  • There is A website, A, that has A POST or get request for liking A video. Client and server authentication is done using cookies (see cookie+ Session in Article 1)
  • Now there is A website B, which is set up by the attacker. It may lure you into the website BY enticing you into the title, or it may implant an IFrame into the website C by means of XSS, etc. No matter which setting, when you enter the corresponding page of B or C, it will automatically request the interface of the website A to like.
  • If we have logged in to website A before and have not logged out, the browser will request the interface of website A with the cookie created when logging in to website A (this is A feature of the browser itself).
  • At this point, it’s like you’re giving a thumbs-up.

This can cause a big problem, for example, by making a video resource very liked and very high up the list, or by marking a resource as a report and causing it to be taken down.

May also cause more serious capital loss problems because of the website is not rigorous, and so on.

At this point, you might see the seriousness of the problem. Because front-end students don’t have to worry about these issues before, it’s easy to ignore these security issues when switching to Node.

How to prevent

If you know the underlying causes from the examples above, you have a general idea of how to prevent them. However, we, as technical developers, cannot force users to log out immediately after every visit because of this problem, which is very unfriendly to users.

Right, so let’s not let the bad guys use cookies to let the server identify the user through the session. That is to say, even if it makes the interface request of A website on B website, the server of A website does not match the user, resulting in unsuccessful login, there is no problem.

Therefore, the token form is particularly suitable.

token

1. What is token

If you look it up on the Internet, you’ll see a lot of explanations. I’d like to put it in a very simple way: on successful login, we generate a unique string that can’t be easily parsed, send it to the client, and the client stores the string and carries it with it on every request for the server to decode.

Does this feel like a cookie plus session? Yes, in principle, it is similar, but the token is generally not put in the cookie, will be put in loaclStorage, for example, even if the bad guy wants to use CSRF way to destroy, but, he can not get token, so he can not make the server authentication for login state, his plot will not succeed.

2. How to do it?

First, generate good tokens in Node

// write a middleware token-middleware.js const setting = require('.. /.. /config/setting');
const verify = require('.. /.. /config/verify');

function tokenMiddleWare(req, res, next) {
    let token = req.headers[setting.token.header];
    if(token === undefined){
        return next();
    }else{// Verify.getToken (token). Then (data => {logger.info())'Check data is :::', data);
            req.data = data;
            return next();
        }).catch(err =>{
            logger.error('Verification error :', err);
            return next();
        })
    }
}
module.exports = tokenMiddleWare;
Copy the code
//setting.js module.exports = {token: {// token key:'test_key_@@'SignTime: 300, // Request header:'authorization', / / don't have to check the routing unRoute: [{url: / \. (JPG | PNG | | js, CSS) $/, the methods: ['GET']]}}}Copy the code
// verify.js
const jwt = require('jsonwebtoken');
const setting = require('./setting'); Const verify = {// Set tokensetToken(username, _id){
        return new Promise(resolve => {
            letToken = jwt.sign(// store data, custom {username, _id}, // key setting. Token. SignKey, {expiresIn: setting.token.signTime, algorithm:'HS256'}); resolve(token); }) }, getToken(token){returnNew Promise((resolve, reject) => {// Handle token stringif(! token.split(' ').length){
                reject({error: 'The token value is empty'})}else{// Decrypt token and return datalet data = jwt.verify(token.split(' ')[1],setting.token.signKey)
                resolve(data)
            }
        })
    }
}

module.exports = verify;

Copy the code

When the login succeeds, set the token:

const verify = require('.. /.. /config/verify'); // logininfo. username -> login name // logininfo. passwd -> login password verify.settoken (logininfo. username, Logininfo.passwd).then(token => {// After generating token, return to client res.json({code: 0, mesg:'success',
        token
    });
});

Copy the code

Then, you need to apply your middleware and Express-JWT to app.js(your root file)

const expressJwt = require('express-jwt'); // Load token middleware app.use(tokenMiddleware); Use (expressJwt({secret: setting. Token. SignKey, algorithms: ['HS256'],
    credentialsRequired: false// requestProperty without token is allowed:'auth'}).unless({// all other urls need to validate path: setting.token.unroute}));Copy the code

Note that when using Express-JWT middleware, you need a bottom-of-the-envelope middleware to handle parsing errors, token expiration, and so on.

If there’s an error, we return 401 by default, so let’s set that up.

app.use(function(err, req, res, next) {// If there is a problem with the token validation, such as a mismatch, expiration, etc., return 401if (err.name === 'UnauthorizedError') { res.status(401).send(err.message); }});Copy the code

This completes the Node layer. Note: the middleware I wrote above is a simple express-jWT function I wrote, so express-jWT can be used without the middleware I wrote.

Second, client-side processing (using VUE)

The client side, the first thing to do, is to save the token returned by the server side, I saved it to loaclStorage. Such as:

res.data.token && localStorage.setItem('authToken', res.data.token);
Copy the code

Then, you need to handle each request header from the front-end to the server:

/ / in the same way, or use axios / / intercept axios. The front end to send request interceptors. Request. Use (config = > {let token = localStorage.getItem('authToken');
    if (token) {
        config.headers['Authorization'] = 'Bearer ' + token;
    }
    return config;
},
error => {
    console.error('Error blocking request', error);
});

Copy the code

In this way, each request from the front-end will carry the Authorization header, which the Node layer will take and parse.

Note also that if the token resolves and returns 401, we also need to continue and go to the login page.

axios.interceptors.response.use(response => {
     return response
    },
    error => {
        if(error.response.status === 401) {
            router.push('/login'); }});Copy the code

Note Note about tokens

To prevent tokens from being used by CSRF, attackers must not obtain tokens through XSS. Therefore, XSS attacks must be handled properly.

conclusion

  • Similar to cookie+session, both deal with HTTP stateless situations
  • Token You can set the header to avoid cookies and prevent login-related cookies from being used
  • Be careful with XSS. XSS is another attack, but if it is XSS, the token is at risk
  • Token and cookie are not a matter of who replaces who, but what is more appropriate in what circumstances.

Okay, so that’s all about the points and pits to avoid when you log in.

I hope you can have a harvest, with more than an hour to read the article and field development test, to solve the novice may need 3 days to study through the problem. If you like it, don’t forget to like it, hahaha