Airing

Airing

哲学系学生 / 小学教师 / 程序员,个人网站: ursb.me
github
email
zhihu
medium
tg_channel
twitter_id

XSS Attack Methods and Defenses

1. XSS Attack Methods#

XSS (Cross-Site Scripting) attacks are the most common web attacks and are a type of code injection attack. Attackers inject malicious scripts into the target website, causing them to run in the user's browser. By exploiting these malicious scripts, attackers can obtain sensitive user information such as Cookies, SessionIDs, etc., thereby compromising data security. The focus is on 'cross-domain' and 'client-side execution'.

The essence of XSS:

  • Malicious code is not filtered and is mixed with the website's normal code;
  • The browser cannot distinguish which scripts are trustworthy, leading to the execution of malicious scripts.

XSS attacks generally fall into the following categories:

  • Reflected XSS
  • Stored XSS
  • DOM XSS
  • JSONP XSS
TypeStorage AreaInsertion Point
Reflected XSSURLHTML
Stored XSSBackend DatabaseHTML
DOM XSSBackend Database / Frontend Storage / URLFrontend JavaScript
JSONP XSSBackend Database / Frontend Storage / URLFrontend JavaScript

1.1 Reflected XSS#

Reflected XSS attacks mainly occur due to the server receiving unsafe input from the client and triggering execution on the client side, thus initiating a web attack.

Specifically, reflected XSS simply "reflects" the user input data back to the browser. This attack method often requires the attacker to lure the user into clicking a malicious link, submitting a form, or entering a malicious website, injecting scripts into the attacked website. This is a non-persistent attack.

For example: On a shopping website, searching for an item will display the search keywords. If the search keyword is filled with <script>alert('handsome boy')</script> and the search is clicked, the page does not filter the keyword, and this code will execute directly on the page, popping up an alert.

1.2 Stored XSS#

Stored XSS attacks are based on submitting content with malicious scripts stored on the server and initiating web attacks when others view this content. The submitted content is usually edited through some rich text editors, making it easy to insert dangerous code.

A common scenario is when an attacker writes an article or comment containing malicious JavaScript code on a community or forum. Once published, all users who access that article or comment will execute this malicious JavaScript code in their browsers. This is a persistent attack.

1.3 DOM XSS#

DOM-based XSS attacks refer to modifying the DOM structure of a page through malicious scripts, which is a purely client-side attack.

The difference between DOM XSS and the previous two types of XSS is that in DOM XSS attacks, the extraction and execution of malicious code are completed on the browser side, belonging to the security vulnerabilities of frontend JavaScript itself, while the other two types of XSS belong to server-side security vulnerabilities. For example:

<input type="text" id="input">
<button id="btn">Submit</button>
<div id="div"></div>
<script>
    const input = document.getElementById('input');
    const btn = document.getElementById('btn');
    const div = document.getElementById('div');
 
    let val;
    
    input.addEventListener('change', (e) => {
        val = e.target.value;
    }, false);
 
    btn.addEventListener('click', () => {
        div.innerHTML = `<a href=${val}>testLink</a>`
    }, false);
</script>

After clicking the Submit button, a link will be inserted into the current page with the address being the user's input. If the user constructs the following content during input:

" onclick=alert(/xss/)

After the user submits, the page code becomes:

<a href onlick="alert(/xss/)">testLink</a>

At this point, when the user clicks the generated link, the corresponding script will execute. DOM XSS attacks are essentially due to the website's frontend JavaScript code itself being insufficiently rigorous, treating untrusted data as executable code. Care should be taken when using .innerHTML, .outerHTML, document.write(), etc., to avoid inserting untrusted data as HTML into the page, and instead use .textContent, .setAttribute(), etc., whenever possible.

Inline event listeners in the DOM, such as location, onclick, onerror, onload, onmouseover, etc., the href attribute of <a> tags, and JavaScript's eval(), setTimeout(), setInterval(), etc., can all execute strings as code. If untrusted data is concatenated into strings passed to these APIs, security vulnerabilities can easily arise, so please avoid this.

1.4 JSONP XSS#

The callback parameter of JSONP is very dangerous, as it has two risks that can lead to XSS:

  1. The callback parameter accidentally truncates js code, with special characters like single quotes, double quotes, and newline characters all posing risks.
  2. The callback parameter maliciously adds tags, causing XSS vulnerabilities.

To ensure cross-domain access security, the browser will by default send a callback parameter to the backend. After the interface receives this parameter, it needs to wrap the returned JSON data with the callback parameter.

The specific return format is:

CALLBACK(JSON)

If the ajax request is a JSONP request, the browser will automatically check the returned content. If it does not return in this format or if the callback content is incorrect, the request will be considered failed.

There is a mechanism where the requested callback will be included in the returned content, which is also a potential problem area. For example, if the returned page has Content-Type: text/html, then the HTML elements injected into the callback can be directly placed on the page. Therefore, HTML pages must not support callbacks. If a link that supports JSONP is directly accessed in the browser, the browser will not perform callback validation.

2. XSS Defense Methods#

2.1 Fundamental Approach to Defending Against XSS#

From the previous introduction, we can see that XSS attacks have two main elements:

  1. The attacker submits malicious code.
  2. The browser executes the malicious code.

The fundamental solution is: filter and escape from input to output.

Input#

Input refers to client request parameters, specifically including:

  • User input
  • URL parameters
  • POST parameters

The encoding method for HTML code is HTMLEncode, which converts strings into HTMLEntities. Currently, to combat XSS, the following six characters need to be entity-escaped.

Special SymbolEntity Encoding
&&amp;
<&lt;
>&gt;
"&quot;
'&#x27;
/&#x2F;

Of course, the above are just the most basic and necessary encodings. HTMLEncode has many more, and specific details can be referenced in the article: Web Security Series (Part 4): Defending Against XSS | Juejin.

In addition, extra attention is needed for rich text input:

  1. First, perform input checks to ensure that the user input is complete HTML code and not concatenated code.
  2. Use htmlParser to parse out HTML code tags, attributes, and events.
  3. Rich text events must be prohibited, as rich text does not require such events; additionally, some dangerous tags also need to be banned, such as <iframe>, <script>, <base>, <form>, etc.
  4. Use a whitelist mechanism to only allow safe tags to be embedded, such as <a>, <img>, <div>, etc. The whitelist applies not only to tags but also to attributes.
  5. Filter user CSS to check for dangerous code.

Output#

Do not assume that filtering during input is sufficient; malicious attackers may bypass defense mechanisms to launch XSS attacks. Generally speaking, all variables that need to be output to HTML pages must use encoding or escaping for defense. The parts that need to be escaped in the output specifically include:

  • Output in HTML
  • Output in JavaScript
  • Output in CSS
  • Output in URL
Output in HTML#

The HTML part uses the same escaping method as input, using HTMLEncode, which will not be repeated here.

Output in JavaScript#

The JavaScript part also requires encoding and escaping, as in JSONP, where unexpected truncation of JSON data or manipulating quotes in the page can cause XSS attacks.

let a = "I am a variable"
// I am a variable = ";alert(1);//
a = "";alert(1);//"

Attackers only need to close the tag to execute the attack. The current defense method is JavaScriptEncode. JavaScriptEncode differs from HTMLEncode in that it requires escaping special characters with \.

Output in CSS#

Attacks in CSS or style tags can be particularly varied. For specific details, refer to: Web Security Series (Part 4): Defending Against XSS | Juejin. Due to space constraints, only the solution will be mentioned here.

To address CSS attack issues, on one hand, strictly control user input of variables into style tags, and on the other hand, do not reference unknown CSS files. If there is a need for users to change CSS variables, the OWASP ESAPI function encodeForCSS() can be used.

Output in URLEncode#

For output in URLs, simply use URLEncode to escape the variable parts.

2.2 Other XSS Defense Methods#

Defense Against JSONP XSS#

  1. Strictly define Content-Type: application/json. Browsers rely on Content-Type for rendering. If the returned content is marked as JSON, even if the body contains HTML tags, the browser will not render it. Therefore, if the interface does not return HTML, do not write it as HTML. So do not misuse Content-Type, and strictly follow the standard protocol. Current frameworks will generally check the content type; if not necessary, do not set it manually, as it may be altered after multiple forwards.
  2. Set a length limit for the callback, which is relatively low; generally, limit function names to 50 characters.
  3. Detect characters in the callback. Generally, the callback should only contain letters and numbers; no other symbols should be present. Function names are only allowed to contain [, ], a-zA-Z0123456789_, $, . to prevent general XSS, UTF-7 XSS, and other attacks.
  4. Filter the callback and JSON data output, following the same principles as output escaping.
  5. Other "tricky" methods include adding other characters (such as /**/, newlines) before the callback output, which does not affect JSON file loading but can prevent other file format outputs to some extent. For example, Gmail used to use AJAX to fetch JSON and would add code like while(1); before outputting JSON to prevent JS remote calls.

Web Security Header Support#

This is a built-in defense capability of browsers, generally effective by enabling web security headers. The specific ones include:

  1. CSP: W3C's Content Security Policy, abbreviated as CSP, is mainly used to define which resources can be loaded on a page, reducing the occurrence of XSS. To configure CSP, one needs to understand the CSP policy strategies; specific details can be referenced in What is CSP.
  2. X-Download-Options: noopen: Enabled by default, disables the Open button in the download dialog in IE to prevent XSS from automatically opening downloaded files in IE.
  3. X-Content-Type-Options: nosniff: Disables IE8's automatic MIME sniffing feature, such as rendering text/plain as text/html, especially when the server's content may not be trustworthy.
  4. X-XSS-Protection: Some XSS detection and prevention features provided by IE, enabled by default.

HttpOnly was first proposed by Microsoft and has since become a standard. Browsers will prohibit JavaScript on pages from accessing cookies with the HttpOnly attribute. Attackers can inject malicious scripts to obtain users' cookie information. Cookies usually contain users' login credentials, and once attackers obtain the cookies, they can initiate cookie hijacking attacks. Therefore, strictly speaking, HttpOnly does not prevent XSS attacks but can prevent cookie hijacking attacks after XSS attacks.

// Using express to set a cookie and enable httpOnly
res.cookie('myCookie', 'test', {
  httpOnly: true
})

Adding CAPTCHA Mechanism#

To prevent scripts from impersonating users to submit dangerous operations.

3. Summary of XSS Experience#

Overall, XSS prevention is very complex and tedious. We need to perform corresponding escaping at all necessary escape points. Additionally, we must prevent excessive and incorrect escaping to avoid garbled normal user input. Although it is challenging to completely avoid XSS through technical means, we can summarize the following principles to reduce the occurrence of vulnerabilities:

The following experience summary is excerpted from "[Basic Skills] Frontend Security Series Part 1: How to Prevent XSS Attacks? | Meituan Technical Team](https://mp.weixin.qq.com/s?__biz=MjM5NjQ5MTI5OA==&mid=2651748921&idx=2&sn=04ee8977545923ad9b485ba236d7a126&chksm=bd12a3748a652a628ecb841f78e00ccf5eb002117236e18a7d947ae824c2cc75841c1f7c0455&mpshare=1&scene=1&srcid=1207x3nOs3EpM656HYO5UcYL%23rd)":

  • Use template engines: Enable the HTML escaping feature provided by the template engine. For example, in ejs, try to use <%= data %> instead of <%- data %>; in doT.js, try to use {{! data }} instead of {{= data }}; in FreeMarker, ensure the engine version is above 2.3.24 and choose the correct freemarker.core.OutputFormat.

  • Avoid inline events: Try not to use concatenated inline event writing like onLoad="onload('{{data}}')" or onClick="go('{{action}}')". Binding events in JavaScript using .addEventListener() is safer.

  • Avoid concatenating HTML: Concatenating HTML on the frontend is relatively dangerous. If the framework allows, use methods like createElement, setAttribute, etc. Alternatively, use mature rendering frameworks like Vue/React.

  • Always stay vigilant: Be alert when inserting into DOM attributes, links, etc., and take strict precautions.

  • Increase attack difficulty, reduce attack consequences: Use methods like CSP, input length configurations, and interface security measures to increase the difficulty of attacks and reduce their consequences.

  • Proactively detect and discover: Use XSS attack strings and automated scanning tools to find potential XSS vulnerabilities.

References#

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.