Thoroughly understand XSS and CSRF

Posted Jun 28, 202010 min read

  1. Introduction to XSS

XSS is called(Cross Site Scripting), which means cross-site scripting attack. At the same time, in order not to confuse Cascading Style Sheets, the cross-site scripting attack is abbreviated as XSS. It is a common vulnerability in web applications. The attacker injects a client-side script(usually JavaScript) into the webpage. Then when theuser browses the webpage**, the script Will be executed to achieve the purpose of the attack.

  1. XSS classification

** Reflected XSS**
The so-called reflection type is that after the content entered by the user in the web page is submitted to the server through the page, the server does not store the datato the database, but reflects it back to the page, which is * What the user enters, the server will display it intact on the page, so that the useris immediately attacked. The most common is the search engine. When we search for a content that cannot be searched, the search engine usually prompts directly on the page that the content cannot be searched. At this time, if the content searched by the user contains some offensive scripts At the same time the server does not process these contents*, then it will be attacked, such as:
Screenshot 2020-06-26 6.31.52.png

** Storage type(Stored XSS)**
The so-called storage type is that after the content entered by the user on the web page is submitted to the server through the page, the server stores the data intact in the database, and reads from the server when other users access the website Take out the data, so that the scripts contained in it are executed, so the storage type is not attacked immediately, but only when other users access the site, but it is stored in the database, so its The attack is more widespread for all visitors of the website. The most common is that a hacker user submits an article, and a script is injected into the article, and the article will be submitted to the database, so when other users access the article, they will be attacked.

The difference between reflective and storage XSS is to see if the data submitted by the user is stored by the server.

* DOM(DOM-based or local XSS)**
The so-called DOM-based type is that the data used does not pass through the server, but is obtained directly from objects such as DOM and Window, for example,document.location,document.URL
, document.referrer, XSS attacks caused by inserting these data into the DOM. It is more common that when we successfully register on a website, we usually jump to a verification page. The verification page content mainly includes how many seconds to automatically jump to a page, or click a link to jump to a page immediately , Such as: http://localhost:3000/validate.html ? redirectTo=javascript:alert('xss'), and our page will *jump based on the incoming redirectTo value, such as:

<script>
    var timer;
    var secs = 10;
    //Get the URL address of the jump
    var redirectTo = location.search.substring(location.search.indexOf("redirectTo=") + 11);
    function go() {
        secs--;
        if(secs >= 0) {
            document.getElementById("secs").innerText = secs + "will automatically jump after seconds";
        } else {
            location.href = redirectTo;
            clearInterval(timer);
        }
    }
    window.onload = function() {
        timer = setInterval(go, 1000);
        document.getElementById("redirect").href = redirectTo;
    }
</script>

<h1 id="secs"></h1>
<a id="redirect">Jump now</a>

There is another one, Client input verification, when the client verifies that the user input is illegal, it will display the user input after the input box and prompt that the input is illegal, such as:

<input id="ipt" type="text" value=""/>
<div id="xss">

document.getElementById("xss").innerHTML = document.getElementById("ipt").value;

At this time, if the user enters , xss attack will also occur.

The key to the XSS attack based on the DOM type is that the data does not pass through the server, but comes from the DOM operation.

Here we may have doubts, can we avoid XSS attacks without using innerHTML and innerText? In fact, the XSS attack does not have much to do with the use of innerHTML. For the client page, we may use innerHTML and innerText to modify the DOM value, but for theserver page, wedo not need to pass innerHTML And innerText to modify the value of DOM**, there will be an example demonstration later, you will understand.

  1. The hazards of XSS

The XSS classification introduced earlier has already mentioned some XSS hazards. It seems that it just popped a few boxes in the user interface, which does not have much effect. However, the amount of damage a vulnerability can cause is not the vulnerability itself, but depends on how the attacker uses the vulnerability. If an attacker just pops a frame on a user's page through the XSS vulnerability, the harm is naturally very small, but XSS is essentially injecting client script into the user's page, and there are many things that client script can do. One of the more harmful is stealing user cookies.

Let's first take a look at the serious consequences of cookie theft. Take Baidu website as an example:
First open the Baidu website, then use the username and password to log in to the Baidu account, such as:
Screenshot 2020-06-27 AM 11.02.52.png

Open the Cookies under Application in the debug window of the webpage, you can see the cookies saved in the current Baidu page, and found cookies with httpOnly, these cookies are often more important, among them BDUSS This is related to login.
Screenshot 2020-06-27 AM 11.06.45.png

Open another browser, enter the Baidu page, at this time is not logged in, also open the webpage debug window, enter document.cookie="BDUSS=BDUSS attribute value viewed in the previous step", such as:
Screenshot 2020-06-27 AM 11.10.19.png

Refresh the page, check the login status of the page, and find that the Baidu page in another browser is also successfully logged in.

So once the attacker gets the user's cookie through the XSS vulnerability, then the user's account can be logged in, and the harm will be very great.

Four, XSS example demonstration

If there is a search page, an input box is provided for the user to search. When the user clicks the search button and enters the search results page, the keyword content and results of the user search will be displayed, such as:

//search page index.html
<form method="GET" action="/search">
    <input type="text" name="keyword" value=""/>
    <button type="submit">Search</button>
</form>

//Normal server code
const express = require("express");
const session = require("express-session");
const app = express();
app.use(session({
    secret:'keyboard cat', //Key string must be set
    resave:false, //reset the expiration time of session cookie with every request
    saveUninitialized:true, //Whether to save the uninitialized session after the user accesses the server, that is, whether to generate a session immediately
    name:"BDUSS", //name of sessionId
    cookie:{
        httpOnly:false //Set the cookie corresponding to SessionId can be obtained by client script
    }
}));
app.use(express.static("public"));
app.use("/search",(req, res) => {
    res.send(`
        <h1>The keyword you are searching for is ${req.query.keyword}</h1>
        <div>Your search results are as follows:</div>
        <h2>!!! You searched for an xss vulnerability!!!</h2>
    `);
});
app.listen(3000);

The server returns a search results page, directly put the user's search keywords on the page to display. It can be seen that the page returned by the server does not use innerHTML and innerText to modify the value of DOM**.

//Hacker server code
const express = require("express");
const app = express();
const cookies = [];
app.use("/cookie",(req, res) => {
    console.log(req.query.cookie);
    if(req.query.cookie) {
        cookies.push(req.query.cookie);
    }
    res.send(`
        <h1>Stolen user cookies:</h1>
        ${cookies}
    `);
});
app.listen(4000);

Now the user enters http://localhost :3000/in the browser to access the search page, and a sessionId will be generated and placed in the cookie(simulated user has logged in)).

When the user enters
The script injected by the hacker is to add a picture with a width and height of 0 to the search results page. When the picture is loaded, it will initiate a request to the hacker server http://localhost:4000/cookie , and will be The attacker's cookie is sent to the hacker server.

Click the search button to enter the search results page, such as:
Screenshot 2020-06-27 PM 12.22.00.png

At this time, the hacker can access the http://localhost:4000/cookie page to see the stolen user cookies, such as:
Screenshot 2020-06-27 PM 12.23.45.png

Five, XSS defense

** Set the cookie used for login to httpOnly to avoid being directly read by the client script.**
For the example mentioned above, the injected script will read the cookie we used to log in through document.cookie, so we need to set the login cookie to httpOnly, such as:

app.use(session({
    secret:'keyboard cat', //Key string must be set
    resave:false, //reset the expiration time of session cookie with every request
    saveUninitialized:true, //Whether to save the uninitialized session after the user accesses the server, that is, whether to generate a session immediately
    name:"BDUSS", //name of sessionId
    cookie:{
        httpOnly:true //Forbid the client to get the cookie corresponding to the session through js
    }
}));

** Filter user input and output, and escape the injected scripts**
For the example mentioned above, although it is no longer possible to read the cookie used for login by setting httpOnly to the cookie, if we enter , we can still pop up the window, For this situation, we need to escape the user's input, you can install a xss module for filtering user input and output, such as:

const xss = require("xss"); //Introduce the installed xss module for data filtering
app.use("/search",(req, res) => {
    res.send(`
        <h1>The keyword you are searching for is ${xss(req.query.keyword)}</h1>
        <div>Your search results are as follows:</div>
        <h2>!!! You searched for an xss vulnerability!!!</h2>
    `);
});

After escaping, the search results page displays as follows:
Screenshot 2020-06-27 PM 4.02.26.png

  1. Introduction to CSRF

The full name of CSRF is(Cross-Site Request Forgery), that is, cross-site request forgery, also known as One Click Attack, a one-click attack, because it usually passes a picture or link and Induce the user to click, when the user clicks on this picture or link, this link usually contains some offensive operations and parameters, the so-called fake request, it willInitiate this forged request to the server as the user's identity. After receiving the forged request, the server will consider it as an operation authorized by the user, resulting in the loss of user data.

Since CSRF is a fake user request, and initiates a request to the server as the user, resulting in the loss of user data, so user must log in to obtain the corresponding login cookie, the fake request will take effect, and will result in user data Lost. So the prerequisites for the CSRF attack are:

  • The user must first log in to the trusted website and generate the corresponding cookie locally;
  • Visited a dangerous website without the user logging out;
  1. Examples and hazards of CSRF

If a user logs in to a website and publishes several articles on it, then visits a dangerous website without logging out, and then clicks on a link in the dangerous website, causing all articles published by him to be delete.
User login first, here is just to simulate user login, so set a user name directly, such as:

app.use("/login",(req, res) => {
    req.session.user = "lihb"; //Simulate user login, directly set a user name
    res.send(`
        <h1>
            Successful login, welcome ${req.session.user} back!
        </h1>
    `);
});

The user enters and visits http://localhost:3000/login in the browser, and then displays the user login success page.
Screenshot 2020-06-27 PM 7.00.47.png

After successful login, visit http://localhost:3000/articles to view the articles published by it.

let ariticles = [//Simulate user article data
    {
        id:1,
        title:"This is article title 1",
        content:"This is article content 1"
    },
    {
        id:2,
        title:"This is article title 2",
        content:"This is article content 2"
    }

];
app.use("/articles",(req, res) => {
if(req.session.user === "lihb") {
if(ariticles.length> 0) {
const template = ariticles.map((article) => {
return <div><h3>${article.title}</h3><p>${article.content}</p></div>
}).join("");
res.send( <h1>The article published by ${req.session.user} is:</h1> ${template} );
} else {
res.send(<h1>${req.session.user} has not published an article yet.</h1>);
}
} else {
res.send(<h1>Please log in first</h1>);
}

});

Screenshot 2020-06-27 PM 6.59.28.png

There is a http://localhost:3000/deleteAll request in the server, you can directly delete all articles, such as:

app.use("/deleteAll",(req, res) => {
    if(req.session.user === "lihb") {
        ariticles = [];
        res.send(`<h1>${req.session.user}'s article was emptied</h1>`);
    } else {
        res.send(`<h1>Please log in first</h1>`);
    }
});

Then visited a dangerous website http://localhost:4000/csrf.html , the dangerous website contains an induced link that will induce the user to click, and the address corresponding to this link is to delete all Article link, once the user clicks this link, it will cause all articles published by him to be deleted.

<!--csrf.html dangerous page-->
<center>
    <a href="http://localhost:3000/deleteAll">Click to receive iQIYI member</a>
</center>

Screenshot 2020-06-27 PM 7.06.14.png

After the user clicks the link above, the displayed page is as follows:
Screenshot 2020-06-27 PM 7.27.36.png

At this time visit http://localhost:3000/articles again and found that the articles have been deleted completely, resulting in user data loss.

Screenshot 2020-06-27 PM 7.28.12.png

So far, a CSRF attack has been simulated. It should be noted that must succeed if the user is already logged in. If we visit the http://localhost :4000/csrf.html page without logging in, then the attack is invalid and the user will be prompted to log in first, such as:
Screenshot 2020-06-27 PM 7.30.57.png

Eight, CSRF defense

The key to the successful CSRF attack is that the server cannot recognize whether the request is authorized by the user. So to defend against CSRF attacks, then we need to let the server know the authenticity of this request.
There are several common ways:
Add token verification:After the user logs in successfully, The server generates a token and returns it to the client, and after the client receives the tokenStore it in cookie or localStorage,* Cookies and localStorage are inaccessible across domains, so attackers cannot get the data in them, and then each time they initiate a request, they *take this token, and the server verifies this token The request will not be answered until it succeeds.

Verify the referer attribute value of the request header:For a request initiated after clicking on a link on a website(not the site itself), the request header will carry a referer attribute with a value of The url address of the website where the click link is located, so we can judge whether the request is from this website according to the referer attribute of the request header, if not, it can be identified as a CSRF attack, such as:

app.use("/deleteAll",(req, res) => {
    //Add referer check in request header
    if(req.headers.referer && !req.headers.referer.includes("http://localhost:3000")) {
        res.send(`<h1>This is a CSRF request that has been intercepted</h1>`);
        return;
    }
    if(req.session.user === "lihb") {
        ariticles = [];
        res.send(`<h1>${req.session.user}'s article was emptied</h1>`);
    } else {
        res.send(`<h1>Please log in first</h1>`);
    }
});