靶场中使用到的php函数以及每种漏洞的防御方式

--------XLL----------

函数 作用 靶场使用场景
$_GET["xxx"] 获取 URL GET 请求参数 Level1~10 获取可控输入 name、keyword、t_sort 等
$_SERVER["HTTP_REFERER"] 获取 Referer 请求头 Level11 读取 Referer 头作为可控输入
$_SERVER["HTTP_USER_AGENT"] 获取 UA 请求头 Level12 读取 User-Agent 头作为可控输入
$_COOKIE["user"] 读取客户端 Cookie 值 Level13 读取 Cookie 可控数据
setcookie() 设置 Cookie Level13 页面初始化 Cookie
htmlspecialchars($str, $flags, $charset) HTML 实体转义,过滤 <>"'& Level2/3/15 输出编码;防御 XSS 核心函数
strtolower() 字符串转小写 Level5 统一小写防大小写绕过
str_replace($find,$rep,$str) 全局单次字符串替换 Level5/6/7 黑名单替换 script、on 等关键字
str_ireplace() 不区分大小写替换 Upload-Labs 大量用于清理后缀、::$DATA
echo 输出内容到页面 所有关卡直接拼接用户输入到 HTML
parse_url() 解析 URL 获取协议、域名 URL 属性上下文防御校验 href 协议
in_array() 判断元素是否在数组(白 / 黑名单) URL 协议白名单校验、后缀校验
json_encode() JS 字符串安全序列化 JS 上下文防御,代替直接拼接变量

--------upload---------

文件上传基础接收类

函数 作用 使用场景
$_FILES['upload_file'] 获取上传文件数组(name/tmp_name/size/type) 全部 20 关接收上传文件
move_uploaded_file($tmp,$dest) 将临时文件移动到目标路径 所有关卡保存上传文件,存在空字节截断、竞争漏洞
basename() 提取纯文件名,清除路径../ 文件路径防御
date() / rand() 生成随机时间戳文件名 随机重命名规避文件名可控风险

字符串清洗 / 后缀处理函数

函数 作用 使用场景
trim() 去除字符串首尾空白(空格、\t、\n) Pass05~09、Pass10 清洗文件名首尾
deldot()(自定义函数) 循环删除文件名末尾所有. Pass05~08 防御末尾点绕过
strrchr($str,".") 截取最后一个.后的后缀 提取文件扩展名用于黑名单校验
pathinfo($file,PATHINFO_EXTENSION) 精准获取文件后缀 Pass19 可控 save_name 后缀提取
explode('.',$str) 按点分割文件名为数组 Pass20 数组参数漏洞拆分文件名
reset($arr) 获取数组第一个元素 Pass20 构造文件名
end($arr) 获取数组最后一个元素 Pass20 校验后缀白名单
count($arr) 获取数组长度 Pass20 稀疏数组逻辑漏洞核心
unlink() 删除服务器文件 校验失败删除非法上传文件
rename() 文件重命名 Pass17/18 竞争漏洞,先保存后重命名

防御方式

XSS 漏洞(反射型 / 存储型 / DOM 型)

反射型 XSS

漏洞成因攻击内容随当前请求进入应用,并在同一次响应中返回。通常需要受害者打开特制链接。XSS-Labs 的大多数 PHP 关卡属于反射型或由请求头触发的服务端 XSS。

存储型 XSS

攻击内容先保存到数据库、文件、缓存或日志,之后在其他用户访问页面时被输出。典型位置包括留言、昵称、 工单、商品描述和后台日志查看器。原始 XSS-Labs 20 关并没有系统覆盖存储型 XSS,因此本课使用一个补充示 例说明。

DOM 型 XSS

DOM 型 XSS 的核心是客户端 JavaScript 把可控数据送入危险 Sink。Payload 可能不出现在服务器响应文本中, 尤其是使用 URL #fragment 时,片段通常不会发送给服务器。

那么我们如何防御呢?

Level 1

在这一关里没有对输入的内容做任何的限制与保护,以至于我们可以直接在输入窗口写入命令,让浏览器执行。

对于这种我们可以把用户输入的内容转换成普通的文本进行输出,例如 htmlspecialchars($str, ENT_QUOTES | ENT_SUBSTITUTE, "UTF-8")

  • $str:需要处理的用户数据(也就是 $name)
  • ENT_QUOTES:同时转义 单引号 '、双引号 " 默认模式只转双引号,如果属性用单引号包裹,会被攻击者逃逸。
  • ENT_SUBSTITUTE 遇到非法残缺 UTF-8 字符,替换为�,不会直接清空字符串。 ❌ 禁止用 ENT_IGNORE(直接删掉异常字符,容易出现安全绕过)
  • | 位或:代表同时开启两个配置
  • "UTF-8":强制指定编码,消除不同 PHP 版本默认编码不一致带来的漏洞。

Level 2

有时候用户的参数写在函数的属性里面,这时候可以通过引号"闭合这个属性,然后再插入一个<script>。

对于这种每一个输出点都要按所在上下文编码。属性值应始终加引号,并使用 ENT_QUOTES 编码双引号和单引号。

网页四大常见上下文:

  1. HTML 正文 <div><?php echo $data ?></div> → 使用带完整参数 htmlspecialchars()(HTML 实体编码)
  2. HTML 属性 <input value="xxx"> → 使用 htmlspecialchars(...,ENT_QUOTES)
  3. <script>标签内部 JS 代码 let a = '数据'不能用 htmlspecialchars ,要用 json_encode() JS 转义
  4. URL 地址 <a href="test.php?key=xxx"> → 使用 urlencode()
    <?php

str = _GET'name';

safe = htmlspecialchars(str,ENT_QUOTES,"UTF-8");

?>

<script>

let name = '<?php echo $safe; ?>'

</script>

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

攻击者传入 payload:\';alert(1)// htmlspecialchars ** 不会转义反斜杠 **,依然能够逃逸,产生 XSS。

Level 5

如果我们在页面上写黑名单,禁止用户写一些高危函数;但是用户还是有可能写一些开发者没见过的有漏洞的函数,或者使用大小写,宽字符,单次删除与双写重组,这种进行绕过。还有直接拼接:<a href="javascript:alert(1)">点击</a> 点击链接执行 JS,产生URL 伪协议 XSS。

对于这种建议直接使用白名单,只允许我同意的函数写入。获取可控 URL;规范化 URL,调用官方 URL 解析器提取 scheme 协议(禁用手写正则匹配)。

为什么手写正则匹配不靠谱 >>> 攻击者大量混淆手段,简单正则 javascript: 轻松绕过;

  • 大小写变形:JaVaScRiPt:alert(1)
  • HTML 实体编码:java&#115;cript:alert(1)
  • 插入空白 / 控制字符:java script:javascript%0A:alert(1)
  • URL 编码变形:javasc%72ipt:alert(1)

1)获取可控 URL

数据来源:GET/POST 参数、用户输入

user_url = _GET'link'; // 用户可控输入,原始数据

此时输入杂乱,可能携带 URL 编码、空白字符、HTML 实体。

2)URL 规范化(Normalize)

什么是规范化?

把各种编码形式、非法空白、转义序列统一还原成标准原始字符串。 作用:消除攻击者用来绕过检测的混淆手段

解析器严格遵循 URL 标准,自动区分: scheme(协议)、host(主机域名)、userinfo、path、query、fragment (# 锚点) 不会被 #、@、? 这类符号欺骗。

规范化要做的事情(通用标准):

  1. URL 百分编码解码 %XX
  2. 剔除 URL 内部无效换行、制表符、空格等控制字符;
  3. 统一大小写(协议统一转小写 JavaScript:javascript:);
  4. 解析并还原 HTML 字符实体(&#xx;)。

举个例子: 混淆载荷:java&#115;cript%0A:alert(1) 经过规范化处理后 → javascript:alert(1) 混淆外壳被剥掉,露出真实协议。

3)调用官方内置 URL 解析器,提取 scheme

什么是 scheme?就是 URL 最前方的协议 http://xxx → scheme = http https://xxx → scheme = https javascript:alert() → scheme = javascript

PHP 内置标准解析函数:parse_url()

这是 C 语言底层实现的标准 URL 解析器,遵循 RFC URL 规范,不依赖开发者写规则,自动拆分 URL 五大组成部分: scheme(协议)、host(域名)、path、query、fragment

Level 11

浏览器发起请求时自动携带 Referer 请求头,标识上一跳页面地址

例:从 A 页面点击链接访问 B 页面,B 收到请求头: Referer: https://a.com/page.html

但是攻击者可以伪造 Referer;使用 Burp Suite、Python requests、curl、Postman 等工具,可随意自定义任意 Referer 内容;浏览器扩展也可以篡改该头部。

如Referer: test"οnclick=alert(1)//

这时候Referer 只可作为辅助信息,不能信任;显示或写入 HTML 时必须按上下文编码。

Level 12:User-Agent 请求头成为输入源

User-Agent(UA)是 HTTP 请求头,用来标识客户端浏览器、设备信息。

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0.0.0

UA 完全可控,攻击者可以随意伪造

User-Agent: <script>alert(document.cookie)</script>

场景复现:Level12 典型漏洞链路(存储型 XSS)

  • 攻击者发送请求,植入恶意 UA;
  • 后端读取 $_SERVER['HTTP_USER_AGENT'] 存入日志 / 数据库
  • 管理员登录后台日志页面,后端读取日志中的 UA;
  • ❌ 没有编码,直接渲染到 HTML 页面;
  • 管理员打开页面,恶意代码执行 → 存储型 XSS

所以我们后台显示页面也要输出编码,入库不需要强制转义,输出渲染 HTML 才必须上下文编码! 防御 XSS 黄金原则:输入不清洗,输出做编码

Level 13:Cookie 成为输入源

1. Cookie 是什么

Cookie 是服务器下发、保存在浏览器本地的小型文本数据(大小上限一般 4KB)。 工作流程:

  1. 浏览器发起 HTTP 请求访问网站;
  2. 服务器响应头通过 Set-Cookie,下发数据给浏览器;
  3. 浏览器自动把这条 Cookie 保存;
  4. 后续每次访问同域名网站,浏览器自动在请求头带上 Cookie 发给服务端。

简单理解:网站存放在你浏览器里的 "小纸条"

响应示例(服务端下发 Cookie)

Set-Cookie: userid=1001; HttpOnly; Secure; SameSite=Lax

后续浏览器请求自动携带:

Cookie: userid=1001

2. Cookie 典型用途

  1. 会话保持(最重要):登录后存放 session 标识,让服务器认识 "你已经登录";
  2. 保存用户偏好:主题样式、语言设置;
  3. 流量统计、广告追踪。

3. 两大分类(安全重点)

① 普通 Cookie(无 HttpOnly)

浏览器 JS 可以通过 document.cookie 读取、修改 👉 如果网站存在 XSS 漏洞,攻击者编写 JS 就能偷走 Cookie,冒充用户登录。

② HttpOnly Cookie

JS 无法访问读取 。 即使出现 XSS,恶意脚本拿不到这条 Cookie; ⚠️【关键:只能防 Cookie 窃取,阻止不了 XSS 代码执行

Cookie 为什么属于不可信输入源?

  1. Cookie 客户端可篡改 用户可通过浏览器开发者工具、脚本、代理工具随意修改 Cookie 的值; 服务端收到的 Cookie,不能默认和下发时内容一致
  2. 部分 Cookie 可被 JS 读取(未开启 HttpOnly) 一旦出现 XSS,JS 可以读取 Cookie 并外送窃取会话凭证。

安全通用铁律:所有从客户端接收的数据(GET/POST/Cookie/ 请求头)全部视为外部不可信输入

那么我们怎么防御呢:

Cookie 输入仍需校验和输出编码。签名可验证完整性;HttpOnly、Secure、SameSite 是会话纵深防御, 不能替代 XSS 修复。

  • Secure 仅在 HTTPS 加密请求中携带 Cookie;HTTP 明文请求不会带上 Cookie。 防止中间人抓包窃取 Cookie。

  • SameSite=Lax/Strict 防御 CSRF 跨站请求伪造,限制跨站点场景自动携带 Cookie。

相关推荐
谁看我谁 狼家二丫2 小时前
局域网文件共享实战:从“账户被禁用”到成功互传文件
开发语言·php
醉城夜风~2 小时前
手撕 C++ string:从零实现一个简易字符串类
开发语言·c++·算法
秋田君2 小时前
QT_QT布局常用类QSplitter窗口分割类与QDockWidget窗口停靠类
开发语言·数据库·qt
老王生涯2 小时前
rust开发环境配置-Windows & GNU
开发语言·windows·rust
Yeauty2 小时前
自建 HLS 第一问:fMP4 还是 TS?用 Rust 在进程内把两种都跑出来
开发语言·后端·rust
AC赳赳老秦2 小时前
软著公开信息批量采集:OpenClaw 抓取软件著作权公开数据,分析企业技术布局方向
大数据·网络·人工智能·python·php·deepseek·openclaw
颜x小2 小时前
[C#]泛型类与泛型方法
开发语言·c++·c#
benben0443 小时前
大模型之基于PEFT的SFT微调实战篇
开发语言·python
Lhan.zzZ3 小时前
深入理解 windeployqt:混合 C++/Qt 项目的打包指南
开发语言·c++·qt