Cross-Site Scripting (XSS)
跨站脚本 (XSS)
描述
跨站脚本(Cross-Site Scripting, XSS)漏洞发生在未经过滤的用户控制输入被提供给用户时。
XSS漏洞会绕过同源策略(Same-Origin Policy, SOP),这是Web安全的核心原则。SOP确保来自http://evil.com的页面无法访问来自http://bank.com的页面内容。
XSS通常分为三个家族:
- Reflected: 用户控制的输入直接反映在页面响应中。
- Stored: 用户控制的输入存储在服务器端(例如数据库中),随后返回给用户。
- DOM-based: 用户控制的输入在客户端被注入到DOM中,从而触发恶意JavaScript的执行。
XSS漏洞允许攻击者执行各种恶意操作,如窃取个人数据(包括用户会话或账户信息),以及代表用户执行操作。
示例
import javax.servlet.http.*;
import java.io.*;
public class VulnerableXSS extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String userInput = request.getParameter("input");
String page = "<html><body><h2>User Input: " + userInput + "</h2></body></html>";
PrintWriter out = response.getWriter();
out.println(page);
}
}
const express = require('express');
const app = express();
app.get('/vulnerable', (req, res) => {
const userInput = req.query.input;
res.send(`<html><body><h2>User Input: ${userInput}</h2></body></html>`);
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
<!DOCTYPE html>
<html>
<head>
<title>XSS Vulnerability</title>
</head>
<body>
<h1>Welcome to the Vulnerable Page</h1>
<p>Search for a product:</p>
<form action="/search">
<input type="text" name="query" placeholder="Enter product name">
<input type="submit" value="Search">
</form>
<p>Search results:</p>
<div id="results">
<!-- Display search results here -->
<?php
// Vulnerable code - directly echoing user input without sanitization
$query = $_GET['query'];
echo "<p>You searched for: " . $query . "</p>";
?>
</div>
</body>
</html>
建议
在一般情况下,防止XSS漏洞需要采取两步保护措施:
- 输入验证: 应对用户控制的输入进行验证,以禁止所有未经授权的字符。例如,电话号码应仅包含数字;姓名应仅包含字母字符等。
- 输出编码: 显示给用户的所有输入应使用经过验证的标准API进行正确编码。强烈建议使用具备原生输出编码支持和安全默认配置的安全模板引擎。
示例
import org.apache.commons.text.StringEscapeUtils;
import javax.servlet.http.*;
import java.io.*;
public class SecureXSSWithLibrary extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
String userInput = request.getParameter("input");
String sanitizedInput = StringEscapeUtils.escapeHtml4(userInput);
String page = "<html><body><h2>User Input: " + sanitizedInput + "</h2></body></html>";
PrintWriter out = response.getWriter();
out.println(page);
}
}
const express = require('express');
const app = express();
const { escape } = require('html-escaper'); // Using 'html-escaper' library
app.get('/secure', (req, res) => {
const userInput = req.query.input;
const sanitizedInput = escape(userInput);
res.send(`<html><body><h2>User Input: ${sanitizedInput}</h2></body></html>`);
});
app.listen(3000, () => {
console.log('Server started on port 3000');
});
<!DOCTYPE html>
<html>
<head>
<title>Secure Page</title>
</head>
<body>
<h1>Welcome to the Secure Page</h1>
<p>Search for a product:</p>
<form action="/search">
<input type="text" name="query" placeholder="Enter product name">
<input type="submit" value="Search">
</form>
<p>Search results:</p>
<div id="results">
<!-- Display sanitized search results here -->
<?php
// Sanitizing user input before displaying
$query = $_GET['query'];
echo "<p>You searched for: " . htmlspecialchars($query, ENT_QUOTES, 'UTF-8') . "</p>";
?>
</div>
</body>
</html>
链接
- CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
- CWE-80: Improper Neutralization of Script-Related HTML Tags in a Web Page (Basic XSS)
- CWE-116: Improper Encoding or Escaping of Output
- CWE-159: Failure to Sanitize Special Element
标准
- OWASP_ASVS_L1:
- V5_2_1
- V5_3_1
- V13_1_1
- OWASP_ASVS_L2:
- V5_2_1
- V1_5_4
- V5_3_1
- V13_1_1
- OWASP_ASVS_L3:
- V5_2_1
- V1_5_4
- V5_3_1
- V13_1_1
- PCI_STANDARDS:
- REQ_2_2
- REQ_6_2
- REQ_6_3
- REQ_6_4
- REQ_11_3
- SOC2_CONTROLS:
- CC_2_1
- CC_4_1
- CC_7_1
- CC_7_2
- CC_7_4
- CC_7_5
- HIPAA_CONTROLS:
- SECURITY212
- SECURITY213
- SECURITY255