PHP跨站脚本攻击怎么防御

wen PHP项目 1

本文目录导读:

PHP跨站脚本攻击怎么防御

  1. 核心防御策略(三大原则)
  2. 具体代码实现(分场景防御)
  3. 进阶防护:内容安全策略(CSP)
  4. 完整防御函数封装(推荐使用)
  5. 其他重要防护措施
  6. 常见绕过手段提醒(注意这些坑)
  7. 最终建议清单

PHP跨站脚本攻击(XSS)是最常见的安全漏洞之一,防御XSS攻击的核心原则是:永远不要信任用户的输入,对所有输出进行编码

下面我将从防御策略具体代码示例进阶防护三个层面,为你提供一套完整的防御方案。


核心防御策略(三大原则)

  1. 输入过滤(Input Validation):验证用户输入的数据格式(如邮箱、数字),拒绝不符合预期的内容。
  2. 输出编码(Output Escaping):将用户数据输出到HTML上下文时,转义特殊字符(如 <, >, , , &)。
  3. 内容安全策略(CSP):通过HTTP头限制浏览器只执行白名单内的脚本,即使攻击脚本被注入也无法执行。

具体代码实现(分场景防御)

场景1:输出到HTML标签内容之间

危险代码

echo "<div>用户评论:".$_POST['comment']."</div>";

如果用户输入 <script>alert(1)</script>,攻击就会执行。

安全做法:使用 htmlspecialchars() 转义。

echo "<div>用户评论:".htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8')."</div>";

参数说明

  • ENT_QUOTES:同时转义单引号和双引号。
  • 'UTF-8':指定字符集,防止宽字节绕过。

场景2:输出到HTML属性值中

危险代码

echo "<img src='".$_GET['img']."' />";

如果用户输入 ' onerror='alert(1),属性注入就发生了。

安全做法:不仅需要转义,还需要验证协议。

$url = $_GET['img'];
if (filter_var($url, FILTER_VALIDATE_URL) && preg_match('#^https?://#i', $url)) {
    // 只允许 http 或 https 开头的 URL
    echo "<img src='".htmlspecialchars($url, ENT_QUOTES, 'UTF-8')."' />";
} else {
    echo "非法 URL";
}

场景3:输出到JavaScript上下文

危险代码

echo "<script>let user='".$_GET['name']."';</script>";

如果用户输入 '; alert(1); ',脚本会逃逸出字符串。

安全做法绝不在JS中直接拼接PHP变量,推荐使用 json_encode() 将数据转为 JSON 格式。

$data = ['name' => $_GET['name']];
echo "<script>let userData = ".json_encode($data, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT).";</script>";

关键点JSON_HEX_* 选项会将特殊字符转为 Unicode 十六进制,防止JS解析器误解。

场景4:输出到URL中

危险代码

echo "<a href='".$_GET['page']."'>跳转</a>";

如果用户输入 javascript:alert(1),会执行JS。

安全做法:使用 filter_var 过滤,或使用白名单。


进阶防护:内容安全策略(CSP)

设置HTTP响应头,告诉浏览器只信任来自特定源的脚本。

header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'");

解释

  • default-src 'self':所有资源默认只能从同源加载。
  • script-src 'self':脚本只能从同源加载(禁止内联脚本和 eval)。
  • 如果使用了前端框架(如Vue/React),可能需要适当放宽 style-src

完整防御函数封装(推荐使用)

建议将输出编码封装为全局函数,避免遗漏。

// 专用于 HTML 上下文输出
function h($string) {
    return htmlspecialchars($string ?? '', ENT_QUOTES, 'UTF-8');
}
// 专用于 URL 属性输出
function u($string) {
    return urlencode($string ?? '');
}
// 专用于 JS 上下文输出
function j($string) {
    return json_encode($string, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
}
// 使用示例
echo "<div>" . h($_POST['name']) . "</div>";
echo "<a href='?page=" . u($_GET['page']) . "'>链接</a>";
echo "<script>const data = " . j($userData) . ";</script>";

其他重要防护措施

  1. 设置 HttpOnly Cookie:防止XSS攻击窃取用户会话(Session)。

    session_set_cookie_params(['httponly' => true]);
  2. 关闭 register_globals(PHP 8 已移除):确保所有变量都通过 $_GET/$_POST/$_SESSION 获取。

  3. 模板引擎自动转义:如果使用 TwigBlade 模板引擎,默认开启自动转义(如 Twig 的 {{ var }} 默认转义)。

  4. 使用现成的安全库(如 HTML Purifier)处理富文本(允许用户输入 <b><i> 等白名单标签):

    composer require ezyang/htmlpurifier
    $purifier = new HTMLPurifier();
    $clean_html = $purifier->purify($dirty_html);

常见绕过手段提醒(注意这些坑)

  • 字符编码问题:必须指定 UTF-8,防止使用 GBK 等编码绕过。
  • 过滤了 <script> 但未过滤事件属性(如 <img onerror>):htmlspecialchars 能防住这种攻击。
  • 未对 javascript: 协议过滤:仅对 URL 和属性进行编码不够,必须加白名单协议。

最终建议清单

开发场景 必须做的操作
任何PHP动态输出 使用 htmlspecialchars()h() 函数
富文本显示 使用 HTMLPurifier 白名单过滤
链接跳转 验证协议(http/https),拒绝 javascript:
AJAX/JSON 返回 使用 json_encode() 且开启 JSON_HEX_*
所有页面 设置 CSP 响应头,全站 HTTPS

“输入过滤”和“输出编码”是两回事。 防御XSS的重点是输出编码,因为你需要允许用户“合理”地输入 <>(比如数学公式),但在输出时将其转为无害文本。

如果你使用的是现代框架(如 Laravel、Symfony),它们自带 e() 辅助函数和模板转义,但原生 PHP 开发时必须严格遵循上述规则。

抱歉,评论功能暂时关闭!