不能只调大 package_max_length 来“解决”大报文攻击,它本质是缓冲区配置项,不是安全策略;盲目增大反而会放大 DoS 风险。

直接结论:不能只调大 package_max_length 来“解决”大报文攻击,它本质是缓冲区配置项,不是安全策略;盲目增大反而会放大 DoS 风险。
为什么调整 package_max_length 不能防攻击
这个配置只是告诉 Swoole “单次接收的 HTTP 包(header + body)最大允许多大”,底层用的是固定大小内存缓存区。它不校验来源、不区分合法上传还是恶意构造的超长 payload,也不做流式解析或分块限速。
常见误操作是看到 Request Entity Too Large 就立刻把 package_max_length 设成 100MB —— 这等于给攻击者开了个内存喷射口:一个并发就能吃掉几十 MB 内存,100 个并发就可能拖垮整台机器。
真正需要的是分层防御:
- 接入层(如 Nginx)提前拦截超大请求(
client_max_body_size) - 应用层(Hyperf)对上传路径做白名单 + 文件类型 + 单文件大小校验
- 协议层(Swoole)用
package_max_length做兜底,但值必须略大于业务最大合法包(比如 20MB),而非无上限
在 Hyperf 中正确设置 package_max_length
该配置必须写在 config/autoload/server.php 的 settings 数组里,且仅对 HTTP server 生效(RPC 或 WebSocket server 需单独配):
'settings' => [
'package_max_length' => 20 * 1024 * 1024, // 20MB,不是 200MB
'enable_coroutine' => true,
'worker_num' => swoole_cpu_num() * 2,
],
注意三点:
- 值必须是整数,单位字节;
20 * 1024 * 1024比硬写20971520更易维护 - 不要在
servers子项里重复设 —— 它属于全局settings,不是 per-server - 改完必须重启服务(
php bin/hyperf.php start),热更新不生效
配合上传逻辑做实际防护
光调 Swoole 参数没用,Hyperf 的上传控制器必须自己校验。例如:
public function upload(UploadFile $file)
{
if ($file->getSize() > 10 * 1024 * 1024) {
throw new BadRequestHttpException('File too large, max 10MB');
}
if (! in_array($file->getMimeType(), ['image/jpeg', 'image/png'])) {
throw new BadRequestHttpException('Unsupported mime type');
}
// 后续保存...
}
关键点:
- 用
$file->getSize()而不是$_FILES—— Hyperf 的UploadFile对象已加载进内存,大小可即时读取 - 校验必须在 move 或写入前完成,否则攻击者可能先占满临时目录
- 如果走 OSS/MinIO 等对象存储,建议用预签名 URL + 前端直传,完全绕过 PHP 层接收大 body
容易被忽略的协程上下文陷阱
在协程环境下,package_max_length 影响的不只是上传:所有 HTTP 请求(包括 POST JSON、表单、甚至恶意构造的超长 header)都会走同一缓冲区。如果你的接口接收大 JSON,又没做 body 解析限长,一样会触发错误。
更隐蔽的问题是:Swoole 默认不校验 Content-Length 是否真实 —— 攻击者发一个 Content-Length: 999999999 但只发几个字节,Swoole 会一直等、直到超时或缓冲区填满。所以务必同时设置:
-
http_parse_post关闭(Hyperf 默认关,避免自动解析爆炸式 form data) -
request_slowlog_timeout(如 5s),及时杀掉卡住的请求 - 用
max_request控制 Worker 寿命,防止长期运行下内存缓慢泄漏
最终防线不在数字大小,而在是否让每个请求都明确知道自己能拿多少、该拒绝什么、以及失败后是否干净退出。


















