上传视频和图片的接口,有没有限制?就是说有没有安全限制,如果没有,岂不是所有人,通过 post,都可以随意的上传视频和图片了吗?
匿名上传在防护上是设计允许的(这是个不需要注册的反馈系统,访客当然要能传图), 但「允许多少、多大」必须有护栏。护栏本身有漏洞。
REQUEST_METHOD
fb_store_mode() === 'off'
fb_local_writable()
fb_csrf_ok()
hash_equals
fb_upload_ext_ok()
.php
.phtml
.svg
fb_upload_mime_ok()
filesize($f['tmp_name'])
is_uploaded_file()
image_max_bytes
video_max_bytes
total_cap_bytes
upload.image_on
video_on
.htaccess
Require all denied
media.php?k=
fb_rate_check('upload')
lib/util.php:26 fb_client_ip():
lib/util.php:26 fb_client_ip()
$keys = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_REAL_IP', 'REMOTE_ADDR']; foreach ($keys as $k) { if (!empty($_SERVER[$k])) { $v = explode(',', (string)$_SERVER[$k]); $v = trim($v[0]); if (filter_var($v, FILTER_VALIDATE_IP)) { return $v; } } }
无条件信任 X-Forwarded-For 等请求头 —— 而这些头是客户端可以随便写的。
X-Forwarded-For
实测(upload_per_hour = 2,连发 6 次、每次换一个 XFF):
upload_per_hour = 2
200,200,200,200,200,200 ← 上限 2,却全部放行
影响面不止上传:fb_ip_hash() 用同一个函数, 所以「发帖频率限制」「验证码频率」「任何基于 IP 的限流」全部失效。 攻击者随便改个头就能无限发帖、无限上传、无限刷验证码。
fb_ip_hash()
正确做法:只有在「明确知道自己在反向代理后面」时才信任这些头, 并且只信任固定层数(例如只取最后一跳的 XFF)。 默认应直接用 REMOTE_ADDR。
REMOTE_ADDR
「领凭证」这一步(POST upload.php 不带 action=put):
POST upload.php
action=put
$size = (int)(isset($_POST['size']) ? $_POST['size'] : 0); ... if ($maxBytes > 0 && $size > $maxBytes) { 拒绝 }
size 是前端自己报的。报一个小数字就能拿到凭证, 随后「真上传」那一步才用 filesize() 实测拦下来。
size
filesize()
结果:拦住的是数据落地,不是请求本身。 攻击者仍可用超大文件反复触发请求、耗带宽和临时磁盘(upload_max_filesize 以内)。
upload_max_filesize
实测:报 size=1060 但实际传 1MB 的图 → 领凭证返回 200。
size=1060
修法:领凭证阶段不承担大小校验职责(本来也做不到), 但真上传阶段必须独立限流,且 post_max_size / upload_max_filesize 要在层配置上卡死(这是服务器层的事,代码层只能兜底)。
post_max_size
fb_rate_hit('upload') 只在领凭证成功时记一次。 action=put(真正写入磁盘/OSS)那一段完全没有 fb_rate_check。
fb_rate_hit('upload')
fb_rate_check
意味着:拿到一个凭证后,可以反复 POST action=put 同一 key 覆盖写入。 虽然单文件大小仍受限,但没有次数约束。
fb_client_ip()
trusted_proxies
fb_rate_check('upload_put', ...)
fb_rate_hit
与 #1(多软件归属)、#2(邮件通知)、#3(表单预填)、#4(附件 0 值)都无关:这是上传接口的访问控制与限流安全,涉及 IP 识别、真上传限流、服务器层配置,可独立验收
No dependencies set.
The note is not visible to the blocked user.
用户提问:上传接口有没有安全限制?会不会所有人都能随意 POST 上传
结论:上传本体防护是够的,但限流可被一行请求头绕过。
匿名上传在防护上是设计允许的(这是个不需要注册的反馈系统,访客当然要能传图),
但「允许多少、多大」必须有护栏。护栏本身有漏洞。
已核实的防护(有效)
REQUEST_METHODfb_store_mode() === 'off'→ 403fb_local_writable()fb_csrf_ok()+hash_equalsfb_upload_ext_ok().php/.phtml/.svg全 400fb_upload_mime_ok()filesize($f['tmp_name'])实测,不信前端报的数is_uploaded_file()image_max_bytes/video_max_bytestotal_cap_bytes(领凭证 + 落地各查一次)upload.image_on/video_on.htaccessv2:Require all denied+ 禁脚本执行media.php?k=fb_rate_check('upload')🔴 漏洞 1:伪造 X-Forwarded-For 可无限绕过限流
lib/util.php:26 fb_client_ip():无条件信任
X-Forwarded-For等请求头 —— 而这些头是客户端可以随便写的。实测(
upload_per_hour = 2,连发 6 次、每次换一个 XFF):影响面不止上传:
fb_ip_hash()用同一个函数,所以「发帖频率限制」「验证码频率」「任何基于 IP 的限流」全部失效。
攻击者随便改个头就能无限发帖、无限上传、无限刷验证码。
正确做法:只有在「明确知道自己在反向代理后面」时才信任这些头,
并且只信任固定层数(例如只取最后一跳的 XFF)。
默认应直接用
REMOTE_ADDR。🔴 漏洞 2:领凭证接口只信前端报的 size
「领凭证」这一步(
POST upload.php不带action=put):size是前端自己报的。报一个小数字就能拿到凭证,随后「真上传」那一步才用
filesize()实测拦下来。结果:拦住的是数据落地,不是请求本身。
攻击者仍可用超大文件反复触发请求、耗带宽和临时磁盘(
upload_max_filesize以内)。实测:报
size=1060但实际传 1MB 的图 → 领凭证返回 200。修法:领凭证阶段不承担大小校验职责(本来也做不到),
但真上传阶段必须独立限流,且
post_max_size/upload_max_filesize要在层配置上卡死(这是服务器层的事,代码层只能兜底)。
🔴 漏洞 3:真上传路径没有限流
fb_rate_hit('upload')只在领凭证成功时记一次。action=put(真正写入磁盘/OSS)那一段完全没有fb_rate_check。意味着:拿到一个凭证后,可以反复 POST
action=put同一 key 覆盖写入。虽然单文件大小仍受限,但没有次数约束。
要做的事
fb_client_ip()默认只信REMOTE_ADDR;新增配置项trusted_proxies(逗号分隔 CIDR),只有请求来自这些地址时才解析 XFF,且只取最右一跳(不可伪造的那一跳)。
fb_rate_check('upload_put', ...)+fb_rate_hit)。size当作「提示值」,不用于放行判断;改为在真上传阶段严格用
filesize()(已如此),并把upload_max_filesize/post_max_size的检查项加入后台「存储体检」提示。验收
为什么单独建单
与 #1(多软件归属)、#2(邮件通知)、#3(表单预填)、#4(附件 0 值)都无关:这是上传接口的访问控制与限流安全,涉及 IP 识别、真上传限流、服务器层配置,可独立验收