PHP and Thinkphp simulated message board to deal with XSS attack (super complete!)

XSS attack principle and protection

brief introduction

XSS(Cross Site Scripting) is a common way in Web attacks. Through this attack, users can be controlled to do a series of malicious operations, such as stealing, tampering, adding users' data or inducing to phishing sites.

Attack principle

A common way is to use unfiltered parameters to pass in some script language code blocks, usually JavaScript, PHP, Java, ASP, Flash, ActiveX, etc., directly to the page or directly into the database. When reading this data through the user's browser, you can modify some information of the current page or steal session and Cookie, so as to complete an XSS attack.

example<script>alert('Javascript Code block')</script><strong οnclick='alert("Surprises")'>Tempting click statement</strong><img src='./logo.jpg' οnclick='location.href="";'/>

The above example is only a rough description of the way, the code will not be so simple in the actual attack


Attack and defense of a storage XSS

Using the xss platform, I use this code to test



Write a platform generated xss script:

<sCrIpt srC=//></sCRipT>

When someone enters the page with the script, the js script will get his cookie and send it to the xss platform.

You just need to log in to xss platform and wait. After you get the cookie, you can log in to his account without password.


For the manifestation of storage xss vulnerability, the classic one is message board. We simulated a message board to test.

The first is the front-end display page board.php

<!DOCTYPE html>
    <meta charset="utf-8">
    <title>Message Board</title>
    <script src=""></script>
    <script type="text/javascript">
            $("#sub").on("click", function(){
                var formData = new FormData();
                var nickname=$("input[name=nickname]").val();
                var content=$("textarea[name=content]").val();
                var email=$("input[name=email]").val();
                if(nickname=="" || content=="" || email=="")
                    alert("Cannot be empty!");
                        //async:false,//false is a synchronization request. If the current request is not completed, the browser may be locked
                            location.reload(true);//Refresh page
    <style type="text/css">
            margin-top: 2%;
            margin-left: 5%;
            margin-right: 5%;
            background-color: rgb(218,244,205);
            text-decoration: none;
        $con = @mysqli_connect('localhost','root','123456','test') or die('Failed to connect to database');
        mysqli_query('set names utf8');
        $sql = "select * from message order by `floor` ASC";
        $res = mysqli_query($con,$sql);
        while($row = mysqli_fetch_array($res)){
            echo '<div class="d" style="margin-top:1%"><div style="background-color: rgb(55,162,113);"></div></div>';
            echo '<div class="d" style="margin-top:1%"><div style="background-color: rgb(55,162,113);"><p style="text-align: right;">'.$row[0].'floor</p></div><p>'.$row['content'].'</p><a href="">'.'<pre style="text-align: right;">Message:'.$row['nickname'].'</a><a href="">'.'    Message time:'.date('Y-m-d H:i:s',$row['time']).'</a></pre></div>';

    <div class="d">
            <input type="text" name="nickname" placeholder="Guest nickname"><br />
            <input type="text" name="email" placeholder="Message sender email"><br />
            <textarea name="content" rows="5" cols="50" placeholder="Message content"></textarea><br />
            <input type="button" name="button" id="sub" value="Submit">


Back end pages to store data add.php


//receive data 
$nickname = @$_POST['nickname'];
$email = @$_POST['email'];
$content = @$_POST['content'];
$time = @time();

$con = @mysqli_connect('localhost','root','123456','test') or die('Failed to connect to database');
mysqli_query('set names utf8');
$sql = "select max(floor) as max from message";
$res = mysqli_query($con,$sql);
$floor = mysqli_fetch_array($res)['max']+1;
$sql = "insert into message(floor,nickname,email,content,time) values($floor,'$nickname','$email','$content','$time')";


As you can see, the four parameters passed in are not processed at all, but stored in the database directly.

So, as long as we type:



After submission, the system will automatically refresh the page and a pop-up box will appear: 1

After clicking OK, you will find that both the message content and the part of the person who left the message are empty.



This is because the js script has been parsed. At this time, we press F12 to open the browser developer tool and find the js script.


So how can developers defend themselves?

Filter keyword script

As a developer, you can easily find that in order to attack xss, you must insert a section of js script. The features of js script are obvious. Script keywords are included in the script, so we only need to filter script.

$nickname = str_replace("script", "", @$_POST['nickname']);//Nickname?

This STR above_ The replace() function means to replace script with null.

As you can see, script is replaced with null and the pop-up box fails.

So how can hackers continue to attack?

The answer is: case bypass


Because js is case insensitive, our case does not affect script execution.

Success pop-up!


Using STR_ The ireplace() function filters script keywords case insensitive

As an excellent developer, if you find a problem, you must correct it in time. It's OK not to distinguish case.

The backend code is modified as follows:

$nickname = str_ireplace("script", "", @$_POST['nickname']);//Nickname?

So, how can hackers bypass it?

The answer is: Double script


The principle is str_ The ireplace() function only finds the script keyword in the middle. The preceding S and the following script are combined to form a new script keyword.


Using preg_replace() function for regular expression filtering script keywords

$nickname = preg_replace( "/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i", "", @$_POST['nickname']);//Nickname?

How can the attacker bypass it again?

The answer is: use the oneerror attribute of the img tag

<img src=x onerror=alert(1)>


Filter alert keywords

See here, I don't know if you're bored. From the perspective of development, I'm a little bored. Don't you like pop ups? I filter the alert keyword to see how you play!

So what should attackers do?

The answer is: Code bypass

<a href=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;


When you click the hyperlink on the page, the box pops up.

This encoding method is character encoding

Character encoding: decimal, hexadecimal ASCII or unicode character encoding, with the style of "&ා value;" for ex amp le, "j" can be encoded as "&ා 106;" or "&ා X6A;"

The above codes are decoded as follows:

<a href=javascript:alert(1)>a</a>

Can you make all the people who enter this page pop-up?

Of course: code with iframe Tags

<iframe src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#49;


In this way, there is neither script keyword nor alert keyword.


Filter special characters

php gives us the htmlentities() function:

$nickname = htmlentities(@$_POST['nickname']);//Nickname?

The htmlentities() function converts characters to HTML entities.

Hackers can no longer attack in the current scenario (in some other scenarios, even if the htmlentities() function is used, it can still be attacked, which is beyond the scope of this article)



From the developer's point of view, an HTML entities() function can basically be used for defense


How to prevent XSS attack with ThinkPHP

PHP is based on ThinkPHP and uses the third-party plug-in htmlpurifier to prevent XSS cross site scripting attacks. You can filter only the specified label (filter the label specified in the rich text editor)

Go to github ezyang/htmlpurifier , composer installation is recommended

$ composer require ezyang/htmlpurifier

But when I was installing it, I always reported an error




We haven't found a solution yet, so we need to use the standard installation method to download the file compression package

Extract the main file directory library, rename it to htmlpurifier, and copy it to the Vendor directory of thinkphp project







In application/common.php In the public function directory, add the following code:

//prevent xss Special methods of attack
function fanXSS($string) {
    require_once '../vendor/htmlpurifier/'; //Modify according to the actual directory path
    // Build configuration object
    $cfg = HTMLPurifier_Config::createDefault();
    // Here is the configuration:
    $cfg->set('Core.Encoding', 'UTF-8');
    // Set allowed HTML label
    $cfg->set('HTML.Allowed', 'div,b,strong,i,em,a[href|title],ul,ol,li,br,span[style],img[width|height|alt|src]');
    // Set allowed CSS Style properties
    $cfg->set('CSS.AllowedProperties', 'font,font-size,font-weight,font-style,font-family,text-decoration,padding-left,color,background-color,text-align');
    // set up a Is it allowed to use on the label target="_blank"
    $cfg->set('HTML.TargetBlank', TRUE);
    // Use configuration to generate objects for filtering
    $obj = new HTMLPurifier($cfg);
    // Filter string
    return $obj->purify($string);


The configuration properties of HTMLPurifier can be found through its website:

1. Configure property selection

The configuration documents of HTMLPurifier are mainly classified into two levels: attr (attribute), HTML(html tag), AutoFormat (auto format), CSS(css configuration), output (output configuration) Small class selection can be completed by adding. And small class name to the large class name.

For example, I want to configure the allowed html tags, such as p tag and a tag, as follows

$cfg->set('HTML.Allowed', 'p,a');

2. Property value selection

In the official document, click a property, you can see the explanation of the property, and you will be told that the value types of the property are String, Int, Array, Boolen

Then you will be told the default value of this property, such as NULL or true or false. The format of this value is the same as that of PHP.

3. White list filtering mechanism

HTML purifier uses the white list filtering mechanism, and only those that are set to be allowed will pass the verification.

4. Basic filter cases

a. Filter out all html tags in the text

$cfg->set('HTML.Allowed', '');

b. Keep the hyperlink tag a and its link address attribute, and automatically add the target attribute with a value of '_ blank’

$cfg->set('HTML.Allowed', 'a[href]');
$cfg->set('HTML.TargetBlank', true);

c. Automatic completion of paragraph code and elimination of useless empty Tags

// Let text automatically label paragraphs, if you must allow P Use of labels
$cfg->set('HTML.Allowed', 'p');
$cfg->set('AutoFormat.AutoParagraph', true);
// Clear empty label
$cfg->set('AutoFormat.RemoveEmpty', true);


Then in the application directory config.php configuration file

Change this filtering method to that method name

'default_filter'         => 'fanXSS',

The above code can be used directly by combining the use of framework and plug-in


You can also filter only some fields

Set the global filter method to the encapsulated htmlspecialchars function:

Modify application/config.php

'default_filter' => 'htmlspecialchars',

Rich text editor content, using the idea of filtering for processing.

For example, the product description field is processed as follows:

//In the function of adding or modifying goods
$params = input();
//Process item description fields separately goods_introduce
$params['goods_desc'] = input('goods_desc', '', 'fanXSS');


Summary of solutions for defense against XSS injection in PHP

1: If PHP directly outputs html, the following methods can be used for filtering:

1.htmlspecialchars function

2.htmlentities function

Three plug-in unit

4.RemoveXss function (Baidu can find it)

2: If PHP is output to JS code or Json API is developed, the front end needs to filter in JS:

1. Try to use innerText(IE) and textContent(Firefox), that is, jQuery's text() to output text content

2. If you have to use innerHTML and other functions, you need to do htmlspecialchars filtering similar to php

3: Other general complementary defense means

1. When outputting html, add Http Header of Content Security Policy

(function: it can prevent the third-party script files from being embedded when the page is attacked by XSS)

(defect: IE or earlier browsers may not support)

2. Add HttpOnly parameter when setting Cookie

(function: it can prevent the Cookie information from being stolen when the page is attacked by XSS, and is compatible with IE6)

(defect: the JS code of the website itself can't operate cookies, and its function is limited, so it can only guarantee the security of cookies.)

3. When developing the API, verify the Referer parameter of the request

(function: can prevent CSRF attack to a certain extent)

(defect: in IE or lower version browsers, the Referer parameter can be forged)


Welcome to QQ to talk about: 965794175

Keywords: PHP SQL encoding Javascript

Added by Nothsa on Thu, 28 May 2020 07:50:18 +0300