应在onmessage中用strpos或preg_match匹配特征码并立即close;复杂场景需自定义protocol的input方法预检;须处理tcp粘包缓存数据,结合连接状态二次防护。

onMessage里做特征码匹配并主动close
Workerman没有内置的“恶意特征码拦截层”,必须在onMessage回调中手动检测。一旦发现匹配,立刻调用$connection->close(),这是最直接、最可控的方式。
不要试图在onConnect或onError里做这事——连接刚建立时还没收到数据;错误发生时可能已经晚了。
- 特征码建议用
strpos()或preg_match(),避免str_contains()(PHP - 匹配前先
trim($data),防止空格/换行干扰判断 - 敏感操作如正则匹配尽量加超时控制,避免被超长畸形包拖垮事件循环
- 如果特征码含二进制内容(如\x00\xFF),确保
$connection->protocol没被误设为text——text协议会提前截断\0
用自定义Protocol类统一处理协议层过滤
当特征码规则变多、需支持黑白名单或动态加载时,硬写在onMessage里会难以维护。此时应实现一个继承Workerman\Protocols\ProtocolInterface的类,在input()方法里做预检。
这个方法会在数据交给onMessage前被调用,返回false即丢弃该包、不触发后续回调;返回完整数据才继续流转。
- 必须返回
string或false,返回null会导致Workerman内部报TypeError - 不要在
input()里调用$connection->close()——此时$connection对象尚未完全初始化,会出错 - 若需记录拦截日志,用
Worker::log()而非error_log(),否则日志可能丢失
配合心跳与连接状态做二次防护
光靠onMessage拦截不够:攻击者可能发合法首包建立连接,再在后续包中嵌入特征码。这时要结合连接生命周期管理,防止被长期占用资源。
- 在
onConnect里给$connection加标记,比如$connection->is_suspicious = false - 每次
onMessage匹配到特征码,设$connection->is_suspicious = true,并记录时间戳 - 用
Connection::$onClose或定时器检查:若is_suspicious为true且连接存活超30秒,强制close()并清空缓存 - 避免在
onClose里再做复杂逻辑——它只保证连接已断开,不保证业务已清理完毕
注意TCP粘包与分包导致的漏判
特征码跨包出现时,单次$data里可能只有一半内容,直接匹配必然失败。这不是配置问题,是TCP协议本身特性。
Workerman默认不做应用层拆包,所以你得自己缓存未完成的数据块。
- 在
$connection上挂载缓冲属性:$connection->recv_buffer ??= '' - 每次
onMessage先拼接:$connection->recv_buffer .= $data - 用
mb_strpos($connection->recv_buffer, $malicious_pattern)查完整特征,命中后substr()截掉已处理部分 - 缓冲区长度超过阈值(如1MB)要主动
close(),防内存耗尽
真正难的不是写判断逻辑,而是想清楚:特征码是按“整包”还是“流式”匹配?是否允许跨包?这些决定了缓冲策略和性能开销。别跳过这步就直接抄代码。











