Hyperf方案 WAF 规则配置

张开发
2026/4/21 3:22:21 15 分钟阅读

分享文章

Hyperf方案 WAF 规则配置
不用外部大库Hyperf 中间件自己实现最灵活Redis 做存储。下面是完整实现---WAF 要防什么 SQL注入 → OR11--把你数据库拖走 esc to interrupt XSS攻击 →script偷Cookie/script路径穿越 →../../etc/passwd 读服务器文件 CC攻击 → 同一IP疯狂刷接口 恶意UA → 扫描器、爬虫、漏洞探测工具 大包攻击 → 上传超大请求体撑爆内存 IP封禁 → 黑名单/白名单---安装 composer require hyperf/redis hyperf/cache---目录结构 app/Middleware/WafMiddleware.php # WAF主中间件 Service/WafService.php # 检测规则核心 Command/WafCommand.php # 管理黑名单命令 config/autoload/waf.php # 规则配置---1.规则配置// config/autoload/waf.phpreturn[enabledenv(WAF_ENABLED,true),// IP白名单永远放行如内网、监控服务whitelist[127.0.0.1,::1],// IP黑名单永远拒绝blacklist[],// 请求体大小限制字节默认1MBmax_body_size1024*1024,// 限流同IP每分钟最多请求次数rate_limit[enabledtrue,max100,// 每分钟100次window60,// 时间窗口秒ban_time300,// 超限后封禁5分钟],// SQL注入检测关键词sql_patterns[/(\bUNION\b.*\bSELECT\b)/i,/(\bSELECT\b.*\bFROM\b)/i,/(\bDROP\b.*\bTABLE\b)/i,/(\bINSERT\b.*\bINTO\b)/i,/(\bDELETE\b.*\bFROM\b)/i,/.*--/,// 注释截断/(\bOR\b|\bAND\b)\s\d\d/i,// OR 11/;.*(\bDROP\b|\bDELETE\b|\bUPDATE\b)/i,],// XSS检测xss_patterns[/script[\s\S]*?[\s\S]*?\/script/i,/javascript\s*:/i,/on\w\s*\s*[\]?[^\]*[\]?/i,// onclick onerror 等/iframe/i,/eval\s*\(/i,/document\.(cookie|write|location)/i,],// 路径穿越检测path_patterns[/\.\.[\/\\\\]/,// ..//etc\/(passwd|shadow)/i,/proc\/self/i,/\x00/,// 空字节注入],// 恶意UA扫描器、漏洞探测bad_ua_patterns[/sqlmap/i,/nikto/i,/nessus/i,/nmap/i,/masscan/i,/zgrab/i,/dirbuster/i,/burpsuite/i,/python-requests\/[0-9]/i,// 按需开启],];---2.WAF 服务检测核心// app/Service/WafService.php?php namespace App\Service;use Hyperf\Redis\Redis;classWafService{private array $config;publicfunction__construct(private Redis $redis){$this-configconfig(waf);}/** * 主检测入口返回拦截原因null表示放行 */publicfunctioninspect(array $request):?string{$ip$request[ip];// 白名单直接放行if(in_array($ip,$this-config[whitelist])){returnnull;}// 黑名单直接拒绝if($this-isBlacklisted($ip)){returnIP已封禁;}// 请求体过大if($request[body_size]$this-config[max_body_size]){return请求体超过限制;}// 恶意UAif($this-matchPatterns($request[ua],$this-config[bad_ua_patterns])){$this-banIp($ip,恶意扫描器);return非法客户端;}// 限流检查if($this-isRateLimited($ip)){return请求过于频繁;}// 把所有用户输入合并检测URL 参数 Body$payload$request[uri]. .$request[query]. .$request[body];if($this-matchPatterns($payload,$this-config[sql_patterns])){$this-banIp($ip,SQL注入);returnSQL注入攻击;}if($this-matchPatterns($payload,$this-config[xss_patterns])){$this-banIp($ip,XSS攻击);returnXSS攻击;}if($this-matchPatterns($payload,$this-config[path_patterns])){$this-banIp($ip,路径穿越);return路径穿越攻击;}returnnull;// 全部通过放行}/** * 限流滑动窗口计数 */privatefunctionisRateLimited(string $ip):bool{if(!$this-config[rate_limit][enabled]){returnfalse;}$cfg$this-config[rate_limit];$keywaf:rate:{$ip};$count$this-redis-incr($key);if($count1){$this-redis-expire($key,$cfg[window]);}if($count$cfg[max]){$this-banIp($ip,CC攻击,$cfg[ban_time]);returntrue;}returnfalse;}publicfunctionbanIp(string $ip,string $reason,int $ttl3600):void{$this-redis-setex(waf:ban:{$ip},$ttl,$reason);// 记录日志$this-redis-lpush(waf:ban_log,json_encode([ip$ip,reason$reason,timedate(Y-m-d H:i:s),]));$this-redis-ltrim(waf:ban_log,0,999);// 只保留最近1000条}publicfunctionunbanIp(string $ip):void{$this-redis-del(waf:ban:{$ip});}publicfunctionisBlacklisted(string $ip):bool{// 配置黑名单 动态封禁returnin_array($ip,$this-config[blacklist])||$this-redis-exists(waf:ban:{$ip});}publicfunctiongetBanLog(int $limit50):array{$logs$this-redis-lrange(waf:ban_log,0,$limit-1);returnarray_map(fn($l)json_decode($l,true),$logs);}privatefunctionmatchPatterns(string $input,array $patterns):bool{// URL解码后再检测防止编码绕过$decodedurldecode($input);foreach($patterns as $pattern){if(preg_match($pattern,$decoded)){returntrue;}}returnfalse;}}---3.WAF 中间件// app/Middleware/WafMiddleware.php?php namespace App\Middleware;use App\Service\WafService;use Psr\Http\Message\ResponseInterface;use Psr\Http\Message\ServerRequestInterface;use Psr\Http\Server\MiddlewareInterface;use Psr\Http\Server\RequestHandlerInterface;use Hyperf\HttpMessage\Stream\SwooleStream;classWafMiddlewareimplementsMiddlewareInterface{publicfunction__construct(private WafService $waf){}publicfunctionprocess(ServerRequestInterface $request,RequestHandlerInterface $handler):ResponseInterface{if(!config(waf.enabled)){return$handler-handle($request);}$body(string)$request-getBody();$reason$this-waf-inspect([ip$this-getClientIp($request),ua$request-getHeaderLine(User-Agent),uri$request-getUri()-getPath(),query$request-getUri()-getQuery(),body$body,body_sizestrlen($body),]);if($reason){return$this-block($reason);}return$handler-handle($request);}privatefunctiongetClientIp(ServerRequestInterface $request):string{// 优先取真实IP经过Nginx代理时return$request-getHeaderLine(X-Real-IP)?:$request-getHeaderLine(X-Forwarded-For)?:($request-getServerParams()[remote_addr]??);}privatefunctionblock(string $reason):ResponseInterface{$responsenew\Hyperf\HttpMessage\Base\Response();return$response-withStatus(403)-withHeader(Content-Type,application/json)-withBody(newSwooleStream(json_encode([code403,message请求被拦截,reason$reason,])));}}---4.管理命令手动封禁/解封// app/Command/WafCommand.php?php namespace App\Command;use App\Service\WafService;use Hyperf\Command\Annotation\Command;use Hyperf\Command\Command as HyperfCommand;#[Command]classWafCommandextendsHyperfCommand{protected?string $namewaf;protected string $descriptionWAF管理: ban/unban/log;publicfunction__construct(private WafService $waf){parent::__construct();}publicfunctionhandle():void{$action$this-argument(action)??log;$ip$this-argument(ip)??;match($action){ban$this-doBan($ip),unban$this-doUnban($ip),log$this-showLog(),default$this-info(用法: php bin/hyperf.php waf ban/unban/log [ip]),};}protectedfunctionconfigure():void{$this-addArgument(action)-addArgument(ip);}privatefunctiondoBan(string $ip):void{$this-waf-banIp($ip,手动封禁);$this-info(已封禁: {$ip});}privatefunctiondoUnban(string $ip):void{$this-waf-unbanIp($ip);$this-info(已解封: {$ip});}privatefunctionshowLog():void{foreach($this-waf-getBanLog()as $log){$this-line([{$log[time]}] {$log[ip]} - {$log[reason]});}}}---5.注册中间件// config/autoload/middlewares.phpreturn[http[\App\Middleware\WafMiddleware::class,// 放最前面第一道关卡// 其他中间件...],];---防御层次图 请求进来 ↓[WAF中间件]← 第一道关 ├─ IP白名单→ 直接放行 ├─ IP黑名单/动态封禁→403├─ 请求体过大→403├─ 恶意UA→ 封IP403├─ 超过限流→ 封IP5分钟403├─ SQL注入特征→ 封IP1小时403├─ XSS特征→ 封IP1小时403├─ 路径穿越→ 封IP1小时403↓ 全部通过[业务逻辑]---常用命令 # 手动封禁IP php bin/hyperf.php waf ban1.2.3.4# 解封IP php bin/hyperf.php waf unban1.2.3.4# 查看封禁日志 php bin/hyperf.php waf log 生产环境建议在 Nginx 层再加 ModSecurityPHP 层 WAF 作为第二道防线两层互补。

更多文章