本文目录导读:

过滤网页恶意代码是一个复杂但非常重要的安全课题,恶意代码通常通过XSS(跨站脚本攻击)、SQL注入、文件包含、命令执行等方式潜入。
由于你无法预知用户或第三方内容会提交什么,必须采用深度防御(Defense in Depth) 的策略,不能仅依赖单一方法。
以下是过滤网页恶意代码的核心原则和实用技术栈:
核心原则:永远不要信任用户输入
这是安全领域的铁律,所有的输入(URL参数、表单数据、HTTP头、上传文件等)和输出(显示在HTML、JavaScript、CSS中的内容)都必须经过处理。
防御策略的具体实现
输入验证(Input Validation)—— 拒绝坏数据
在数据进入系统之前,严格检查其格式和内容。
- 白名单验证(推荐): 只允许已知安全的字符或模式。
- 示例: 用户年龄字段只允许数字
[0-9];用户名只允许字母、数字和下划线[a-zA-Z0-9_]。
- 示例: 用户年龄字段只允许数字
- 黑名单验证(不推荐): 尝试检测已知的恶意关键字(如
<script>、alert、javascript)。- 缺点: 攻击者可以通过编码、大小写混写、注释等方式轻松绕过(
<ScRipT>、\u006Aavascript)。
- 缺点: 攻击者可以通过编码、大小写混写、注释等方式轻松绕过(
- 长度限制: 对输入字符串设置最大长度,防止缓冲区溢出或大量数据消耗。
输出编码(Output Encoding)—— 转义坏数据
这是防止XSS最核心的手段,根据数据将要出现的上下文,使用不同的编码方式。
- HTML上下文(标签内或属性值): 使用HTML实体编码。
<>转义为<和>- 库:
htmlspecialchars()(PHP),escapeHtml()(Java/JSTL),html.escape()(Python)
- JavaScript上下文(字符串或代码中):
- 需要将特殊字符转义为Unicode或十六进制。
- 转义为
\x22或\u0022。 - 注意: 永远不要使用
innerHTML插入用户数据,应使用textContent或createTextNode。
- URL上下文(链接参数):
- 使用
URL编码(百分号编码),将空格变为%20,&变为%26。 - 关键: 验证URL的协议(Protocol)必须是
http://或https://,禁止javascript://或data://。
- 使用
- CSS上下文: 非常危险,尽量避免,如果必须,使用严格的白名单。
内容安全策略(CSP)—— 浏览器层面的“防火墙”
CSP是一个HTTP响应头,它告诉浏览器哪些资源(脚本、样式、图片)是允许加载和执行的,这是防御XSS的第二道坚固防线。
- 工作原理: 服务器设置
Content-Security-Policy头。 - 示例:
Content-Security-Policy: default-src 'self'; script-src 'self' https://trusted-cdn.com; object-src 'none'default-src 'self': 只允许从同域名加载资源。script-src 'self' ...: 只允许执行来自本域名和指定CDN的脚本,禁止内联脚本(即<script>alert(1)</script>)。object-src 'none': 禁止加载Flash等插件(这是很多老漏洞的来源)。
- 最佳实践: 先使用
Content-Security-Policy-Report-Only模式监控,再正式启用。
使用安全的库和模板引擎
不要手动拼接字符串,这极易出错。
- 前端框架(自动转义): React (JSX)、Vue、Angular 默认对绑定的数据进行转义,React 中的
{userInput}会自动转义HTML。 - 模板引擎: 使用带有自动转义功能的模板(如 Jinja2 的自动转义、Go的
text/template和html/template)。 - 富文本编辑器(特殊情况):
- 如果你允许用户使用
Bold、Italic等富文本,不能直接转义所有HTML。 - 必须使用 HTML 清理器(HTML Sanitizer),如:
- 前端:
DOMPurify(推荐)、sanitize-html - 后端:Java 的
OWASP Java HTML Sanitizer、Python 的bleach、PHP 的HTML Purifier,这些工具会保留安全标签(如<b>、<i>、<a>)并删除所有危险的属性(如onclick)和标签(如<script>)。
- 前端:
- 如果你允许用户使用
针对特定攻击的过滤
- SQL注入: 永远不要拼接SQL语句,使用参数化查询(Prepared Statements) 是唯一的解决方案。
- 错误示例:
"SELECT * FROM users WHERE name = '" + userName + "'" - 正确示例 (PDO in PHP):
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name"); $stmt->execute(['name' => $userName]);
- 错误示例:
- 文件上传:
- 检查文件扩展名和MIME类型。
- 将文件存储在Web根目录之外,通过一个脚本来分发(这样可以控制响应头,禁止执行)。
- 对图片进行
重新编码(Resample / Re-compress),可以有效清除图片中的恶意代码(隐写术除外)。
- 命令执行(OS Command Injection):
- 尽量避免使用
exec()、system()这类函数。 - 如果必须使用,严格对输入进行转义(例如使用
escapeshellarg()或escapeshellcmd())。
- 尽量避免使用
过滤步骤的决策流程图(伪代码)
用户输入数据 ->
1. 输入验证:
- 是使用白名单验证吗? (邮箱、数字、枚举)
- 是:如果通过,进入下一步;否则,拒绝请求。
- 否 (富文本、自由文本):进入下一步。
2. 上下文判断:
- 这个数据要放到哪里?
- A. HTML标签体: -> HTML实体编码 (htmlspecialchars)
- B. JavaScript变量: -> Unicode转义 / JSON序列化
- C. 链接: -> URL编码 (urlencode)
- D. SQL语句: -> 使用参数化查询 (绑定变量)
3. 针对富文本:
- 使用 HTML清理器 (如 DOMPurify) 移除危险标签和属性。
4. 输出:
- 设置 CSP 头 (Content-Security-Policy)。
- 使用安全的框架 (如 React/Vue) 输出。
总结建议:不要自己写过滤函数
- 使用业界公认的库:不要试图用正则表达式过滤
script,攻击者总有办法绕过。 - 前后端双重过滤:前端过滤为了用户体验(快速提示错误),后端过滤是为了安全(因为攻击者可以绕过前端直接发送请求)。
- 关注最新漏洞:关注 OWASP(开放式Web应用程序安全项目)Top 10 列表,每年都会更新。
- 安全扫描:使用自动化工具(如 Burp Suite、OWASP ZAP)对网站进行定期扫描。
最后的核心:
- 输入 -> 验证(白名单)
- 输出 -> 转义(按上下文)
- 架构 -> CSP + 参数化查询 + 最小权限
- 心态 -> 假设所有数据都是恶意的。