如果你手上正好跑着一批 Squid 代理缓存节点那么这几天你应该留意一下 CVE-2025-54574 这个编号。这个漏洞指向的是 Squid 在处理特定 HTTP 请求时存在拒绝服务风险攻击者不需要任何认证就能触发严重情况下可以直接把 Squid 进程打崩溃进而拖垮整个代理服务和依赖它的业务链路。我最初看到这个漏洞信息时第一反应是查受影响版本、看官方补丁、评估自己环境里的暴露面但随后就意识到一个更现实的问题很多团队在漏洞曝光的第一时间根本没有升级窗口要么是业务压着不批变更要么是线上版本和补丁存在兼容性风险。于是就有了这篇文章要聊的东西——一套围绕 CVE-2025-54574 编写的 Squid 高危漏洞缓解脚本以及它背后的设计思路和落地经验。这篇文章适合正在维护 Squid 服务的一线运维和安全工程师参考也适合那些第一次接触应急缓解脚本、想知道“除了升级还能做什么”的读者。我会把漏洞影响面、为什么不先升级、脚本怎么写、怎么验证、怎么回滚以及我在实际部署中踩过的坑一次性讲清楚保证你可以照着抄、抄完能跑、跑完能收。1. 漏洞扫描亮红灯CVE-2025-54574到底影响什么1.1 这是一个什么类型的漏洞CVE-2025-54574 是 Squid 在请求处理流程中暴露出的拒绝服务漏洞。Squid 作为老牌代理服务器核心工作就是接收客户端请求、解析请求头和请求体、查询缓存、转发上游、返回响应。这个处理链路里涉及大量内存分配、缓冲区拷贝、超时管理和并发状态控制任何一个环节对畸形输入处理不够严谨都可能成为攻击面。根据现有信息CVE-2025-54574 的触发点就在请求解析和连接处理的交界处攻击者构造特定的 HTTP 请求让 Squid 走到某个异常分支时触发断言失败或内存状态错乱最终表现为 worker 进程退出。这类问题最麻烦的地方在于它不需要什么高级技巧。攻击者只要能跟 Squid 建立 TCP 连接并发出精心构造的请求即可不需要账号、不需要特殊权限、不需要先拿到内网某个跳板。一个公网可达的 Squid 端口就等于一个暴露给所有人的攻击入口。漏洞评级为高危CVSS 分通常落在 7.5 上下理由是“远程可利用 影响可用性 无需认证”——这三条组合在一起足以让安全团队把处理优先级提到最高。1.2 哪些环境和场景最容易中招最容易中招的倒不是那种只在办公室局域网里跑着的小代理而是三种典型场景。第一种是 CDN 或边缘节点上挂着 Squid 做反向加速这类节点直接面对公网流量攻击者随手扫一下端口就能发现。第二种是企业出口网关上的正向代理内网所有终端的 HTTP 流量都过它一旦崩溃全公司上网直接瘫痪。第三种是容器平台或大数据集群里的 Squid 缓存实例看起来不起眼但很多中间件会通过它拉取镜像或软件包挂了之后整个发布流程都会卡住。我在排查自己环境时习惯第一时间明确三个问题Squid 版本是多少、监听端口是否对外暴露、有没有其他服务依赖这个实例。版本信息直接决定漏洞是否命中暴露面决定风险等级依赖关系决定应急策略优先级。如果版本在受影响区间且端口公网可达那基本可以判定为紧急状态需要立即启动缓解措施。2. 为什么不等补丁、先上缓解脚本2.1 升级窗口不是你想开就能开很多人会问漏洞出来了直接升级不就行了吗理论上确实如此但实际生产环境里升级一个 Squid 涉及的事远比“替换二进制文件”复杂。Squid 通常承担着缓存策略、访问控制、认证对接、日志采集等职责和周边系统有大量隐式耦合。新版本可能调整配置项默认值可能改变某些访问控制指令的解析顺序甚至可能对内存占用和文件描述符需求提出更高要求。贸然升级很可能漏洞堵住了但业务流量先崩了。更重要的是窗口期。安全通告刚出来的时候官方补丁未必同步发布即便发布了也需要经历测试环境验证、灰度环境试点、生产环境分批发布这套流程。一个中等规模的公司这套流程走下来至少需要一到两个变更窗口。而漏洞是不等人的从公开 PoC 到大规模利用的间隔可能非常短。这中间的空窗期就是缓解脚本发挥作用的时候。2.2 缓解脚本的定位争取时间而不是替代升级缓解脚本的目标非常明确在不升级、不重启、不打乱业务的前提下从网络层和应用层同时收缩攻击面把漏洞被利用的概率压到最低。它不是设一个参数就完事而是从流量入口就开始限制再配合 Squid 自身的配置加固形成两道屏障。第一道屏障在网络层。Squid 这类拒绝服务漏洞要被打成功攻击者必须先建立大量连接或者发送一批恶意请求。如果我们在防火墙层面对 Squid 端口的“新建连接速率”做限制攻击者想在短时间内快速探测或触发漏洞就变得非常困难。第二道屏障在应用层。我们可以通过调整 Squid 配置里的请求头大小、管道预取数量等参数压缩恶意请求的生存空间让攻击者在碰触到漏洞触发点之前就被 Squid 自身的解析逻辑拦截或拒绝。这套组合拳的核心词是“留退路”。所有动作都要可逆所有变更都要可回滚所有策略都要先考虑会不会误伤正常流量。应急不是搞实验先把服务保住再把时间抢出来等补丁验证完毕再平滑升级这才是正确的节奏。3. 缓解脚本设计原则优先保业务、留退路3.1 为什么必须把“可回滚”放在第一位我在写这个缓解脚本之前给自己定了几条硬性要求执行要快、不重启服务、不改变现有缓存内容、一条命令能收回。为什么这么较真因为应急脚本本身就是高危变更如果它比漏洞还先引发事故那就本末倒置了。具体来说脚本对 Squid 配置的改动要尽可能采用“追加”而不是“重写”。我会把缓解参数写成一个带标记的配置片段追加到 squid.conf 末尾并在片段开头和结尾留下唯一标识。回滚时只需要用 sed 按标识删除这个区间再执行squid -k reconfigure热加载即可完全不影响原配置内容。这种做法的好处是你随时可以对比“变更前”和“变更后”的配置差异也方便审计人员核查。3.2 网络层限制管住“新建连接”而不是“总连接数”很多人在做流量限速时习惯直接对端口总连接数设上限这是一个误区。Squid 是长期运行的代理服务维持大量长连接是正常状态尤其是浏览器和移动端会主动复用连接。如果限制总连接数高峰期很容易误伤正常用户。真正能体现攻击特征的指标是“新建连接速率”。CVE-2025-54574 这类漏洞利用过程必然伴随高频的恶意请求尝试表现出来就是单位时间内的新建连接数量异常猛增。所以在防火墙规则里我优先对 TCP flag 为 SYN 的新建连接做限速对已经建立的连接直接放行。这样正常用户的长连接不受影响而攻击者的短平快探测会被明显压制。3.3 白名单机制先把自己人放进来网络层限流最大的副作用是可能把运维自己挡在外面。如果你在改防火墙规则时把自己的 IP 也限流了Squid 出问题之后想上去排查都进不去。因此脚本必须支持运维网段白名单凡是匹配白名单的源地址一律跳过限流规则。这个设计不能省生产事故里“把自己锁在门外”的教训太多了。4. 一套可直接抄的Squid漏洞缓解脚本4.1 脚本完整实现我下面给出的是一个可直接落地的 bash 脚本支持apply、remove、status三个动作。apply下发缓解配置remove移除配置回滚status检查当前状态。默认监听端口是 3128如果你的 Squid 跑了多个端口或改了端口修改开头的SQUID_PORT变量即可。#!/usr/bin/env bash # # CVE-2025-54574 Squid 高危漏洞应急缓解脚本 # 用法: sudo bash cve-2025-54574-mitigation.sh {apply|remove|status} # 作用: 网络层新建连接限流 Squid 应用层配置加固 # set -euo pipefail SQUID_PORT${SQUID_PORT:-3128} SQUID_CONF${SQUID_CONF:-/etc/squid/squid.conf} SQUID_BIN$(command -v squid || echo /usr/sbin/squid) NFT$(command -v nft || true) IPT$(command -v iptables || true) # 运维白名单网段空格分隔例如 10.0.0.0/8 192.168.0.0/16 ADMIN_CIDR${ADMIN_CIDR:-} # 新建连接限速参数 NEW_CONN_RATE${NEW_CONN_RATE:-50} NEW_CONN_BURST${NEW_CONN_BURST:-100} TAG_START# CVE-2025-54574-MITIGATION-START TAG_END# CVE-2025-54574-MITIGATION-END TABLE_NAMEsquid_cve_54574 IPT_CHAINSQUID_CVE_2025_54574 LOG_FILE/var/log/cve-2025-54574-mitigation.log log() { echo [$(date %Y-%m-%d %H:%M:%S)] $* | tee -a $LOG_FILE } check_root() { if [ $(id -u) -ne 0 ]; then log 请使用 root 权限运行 exit 1 fi } check_squid() { if [ ! -f $SQUID_CONF ]; then log 未找到 Squid 配置文件 $SQUID_CONF exit 1 fi if ! command -v $SQUID_BIN /dev/null 21; then log 未找到 Squid 可执行文件 exit 1 fi } backup_conf() { local bak_file$SQUID_CONF.bak.cve-2025-54574-$(date %s) cp -a $SQUID_CONF $bak_file log 配置文件已备份到 $bak_file } apply_nftables() { local admin_set${ADMIN_CIDR// /,} [ -n $admin_set ] || admin_set255.255.255.255/32 nft list table inet $TABLE_NAME /dev/null 21 nft delete table inet $TABLE_NAME nft -f - EOF table inet $TABLE_NAME { set admin_cidr { type ipv4_addr flags interval elements { $admin_set } } chain input { type filter hook input priority filter - 10; policy accept; meta l4proto tcp tcp dport $SQUID_PORT ip saddr admin_cidr accept meta l4proto tcp tcp dport $SQUID_PORT ct state established,related accept meta l4proto tcp tcp dport $SQUID_PORT ct state new limit rate ${NEW_CONN_RATE}/second burst ${NEW_CONN_BURST} packets accept meta l4proto tcp tcp dport $SQUID_PORT ct state new drop } } EOF log nftables 规则已下发: 端口 $SQUID_PORT, 新建连接限速 ${NEW_CONN_RATE}/s, 突发 $NEW_CONN_BURST } apply_iptables() { iptables -N $IPT_CHAIN 2/dev/null || iptables -F $IPT_CHAIN iptables -D INPUT -j $IPT_CHAIN 2/dev/null || true iptables -A INPUT -j $IPT_CHAIN for cidr in ${ADMIN_CIDR:-255.255.255.255/32}; do iptables -A $IPT_CHAIN -p tcp --dport $SQUID_PORT -s $cidr -j ACCEPT done iptables -A $IPT_CHAIN -p tcp --dport $SQUID_PORT -m state --state ESTABLISHED,RELATED -j ACCEPT iptables -A $IPT_CHAIN -p tcp --dport $SQUID_PORT -m state --state NEW -m limit --limit ${NEW_CONN_RATE}/second --limit-burst $NEW_CONN_BURST -j ACCEPT iptables -A $IPT_CHAIN -p tcp --dport $SQUID_PORT -m state --state NEW -j DROP log iptables 规则已下发: 端口 $SQUID_PORT, 新建连接限速 ${NEW_CONN_RATE}/s } apply_squid_conf() { if grep -q $TAG_START $SQUID_CONF; then log Squid 配置片段已存在跳过追加 else cat $SQUID_CONF EOF $TAG_START # 应急缓解参数正式升级后可移除整个区间 request_header_max_size 32 KB request_body_max_size 1 MB pipeline_max_prefetch 0 client_request_timeout 30 seconds pconn_timeout 120 seconds snmp_port 0 $TAG_END EOF log Squid 配置片段已追加 fi } verify_squid() { if $SQUID_BIN -k parse 2/tmp/squid-parse-error; then log Squid 配置解析通过 else log Squid 配置解析失败: $(cat /tmp/squid-parse-error) exit 1 fi } reload_squid() { if $SQUID_BIN -k reconfigure 2/dev/null; then log Squid 已热加载配置无需重启进程 else log Squid 热加载失败请手动执行 $SQUID_BIN -k reconfigure fi } apply() { check_root check_squid backup_conf if [ -n $NFT ]; then apply_nftables elif [ -n $IPT ]; then apply_iptables else log nftables 和 iptables 均不可用仅执行 Squid 配置加固 fi apply_squid_conf verify_squid reload_squid log CVE-2025-54574 缓解措施已启用 } remove_nftables() { if [ -n $NFT ] nft list table inet $TABLE_NAME /dev/null 21; then nft delete table inet $TABLE_NAME log nftables 规则已移除 fi } remove_iptables() { if [ -n $IPT ]; then iptables -D INPUT -j $IPT_CHAIN 2/dev/null || true iptables -F $IPT_CHAIN 2/dev/null || true iptables -X $IPT_CHAIN 2/dev/null || true log iptables 规则已移除 fi } remove_squid_conf() { if grep -q $TAG_START $SQUID_CONF; then sed -i /$TAG_START/,/$TAG_END/d $SQUID_CONF log Squid 配置片段已移除 fi } remove() { check_root remove_nftables remove_iptables remove_squid_conf if [ -f $SQUID_CONF ]; then verify_squid || true reload_squid || true fi log CVE-2025-54574 缓解措施已关闭 } status() { echo Squid 进程状态 pgrep -a squid || echo Squid 未运行 echo echo nftables 规则 if [ -n $NFT ] nft list table inet $TABLE_NAME /dev/null 21; then nft list table inet $TABLE_NAME else echo 无 nftables 缓解规则 fi echo echo iptables 规则 if [ -n $IPT ]; then iptables -L $IPT_CHAIN 2/dev/null || echo 无 iptables 缓解规则 fi echo echo Squid 配置标记 grep -n $TAG_START $SQUID_CONF 2/dev/null || echo 配置中无缓解标记 } case ${1:-} in apply) apply ;; remove) remove ;; status) status ;; *) echo 用法: $0 {apply|remove|status}; exit 1 ;; esac4.2 脚本关键参数怎么调先看NEW_CONN_RATE和NEW_CONN_BURST。默认值分别是每秒 50 个新建连接、突发上限 100 个。这个数值对大多数内网代理场景是够用的但如果你的 Squid 本身就是高并发入口比如 CDN 边缘节点50/s 可能会挡住正常流量。建议在业务低谷期先跑一版 status用ss -s或nft list table观察现有连接数和新建速率再按基线的 1.5 到 2 倍设定限速值。宁可一开始放宽一点确认业务稳定后再逐步收紧。再看 Squid 配置片段里的参数。request_header_max_size 32 KB会限制单个请求头总大小正常业务很少超过这个值但如果你有大量自定义 header 或单点登录票据需要先确认没超限。request_body_max_size 1 MB是对请求体做限制这个值对反向代理场景比较安全但对正向代理或者有上传业务的场景你要么调大要么删掉这一行否则用户传大文件会直接失败。pipeline_max_prefetch 0是关闭 HTTP 管道预取能有效降低内存压力代价是代理无法提前预读多个请求对整体吞吐略有影响属于可以接受的权衡。4.3 为什么用 nftables 优先iptables 作为兜底现在的 Linux 发行版基本都切到了 nftables 框架iptables 命令本身也只是一个兼容翻译层。nftables 的好处是规则集可整体替换、语法清晰、支持 ipset 风格的集合尤其适合写白名单。我的脚本优先探测 nft 命令没有才回退 iptables这样能覆盖从 CentOS 7 到 Ubuntu 24.04 的绝大多数环境。网络层规则的核心逻辑只有三条白名单直接放行、已建立连接放行、新建连接限速。限速那一行用了 limit 规则它会在突发超过burst阈值后开始丢弃超出部分。对于攻击者来说这种策略最难受的地方在于它不会让连接立刻失败而是让它偶尔成功、偶尔被丢打乱了漏洞利用所需的稳定节奏。5. 缓解效果验证别等被打挂了才后悔5.1 下发规则后先做三层检查脚本执行完 apply第一件事不是走人而是验证。我习惯按三层做检查。第一层看防火墙规则是否真的在生效nft list table inet squid_cve_54574确认表存在规则链里有 accept 和 drop 的匹配项。第二层看 Squid 配置是否加载成功squid -k parse没有报错squid -k check确认运行中同时看 cache.log 最后几行是否出现Reconfiguring关键字如果出现了说明热加载完成。第三层做真实业务探测用代理访问几个内外部站点确认正常响应再模拟一个不带认证的畸形请求或者高频并发请求观察峰值新建连接是否被防火墙拦截。5.2 日志和监控怎么配合缓解脚本只是被动防御你还需要知道它有没有在关键时刻发挥作用。我建议在 Squid 的 cache.log 里关注FATAL、assert、excessive这几个关键字出现任何一个都说明服务可能正在被针对。firewalld 或 nftables 的日志可以先不开因为会刷屏如果确实要记录拦截事件可以在 nft 规则里加log prefix SQUID-RATE-LIMIT然后通过 journalctl 过滤。我还额外加了一个 1 分钟的 systemd timer 或 crontab用来检查 squid 进程是否存活。一旦发现进程退出立即执行squid -k restart拉起服务并发一条告警到钉钉或企业微信群。应急期间别信人的反应速度要让监控系统和重启机制先跑起来。6. 实际落地时最容易踩的坑6.1 限速参数拍脑袋业务先被误伤这是我在测试环境里第一个踩到的坑。我一开始把NEW_CONN_RATE设成 10/s心想这总够用了吧结果压测工具一跑大量连接直接超时正常爬虫抓取也频繁失败。后来用ss -ant | grep :3128 | wc -l看了下基线才发现平时的新建连接峰值就有四五十。所以这个参数必须基于被防护实例的真实流量基线来设置不能照抄文档默认值。调参的正确姿势是先观察一周记录高峰时段的新建连接数然后设定为峰值均值的 1.5 到 2 倍再留 100 左右的突发缓冲。6.2 Squid 配置片段里 request_body_max_size 的误伤在一台有文件上传业务的反向代理上我把request_body_max_size 1 MB直接写进配置结果用户上传超过 1MB 的文件全部 413。虽然这个参数在应急场景下压缩了漏洞利用空间但对业务伤害太大。后来我改成只在纯正向代理或纯缓存场景启用反向代理和有上传需求的场景干脆删掉这一项靠着网络层限流来兜底。这也提醒我应急脚本里每个参数都要考虑业务兼容性不能“一刀切”。6.3 多端口实例只防护了一个端口Squid 通过http_port指令可以同时监听多个端口有的实例还会额外开https_port做终止 TLS 代理。我的脚本默认只处理 3128如果环境里有第二个监听端口网络层规则根本不会覆盖它。排查时发现攻击者完全可能从没被限制的端口发起请求风险照样存在。现在的做法是在脚本开头把端口参数放到一个数组里遍历生成规则或者干脆用ss -lntp | grep squid先找出所有监听端口再下发。6.4 配置解析失败导致热加载中断有一次我在存量老版本 Squid 上执行 apply发现snmp_port 0这个参数解析报错因为那个版本根本不支持这条指令。脚本虽然做了squid -k parse校验但如果校验失败就直接退出不会把前面的网络层规则和配置片段残留状态清理干净。后来我在脚本里加了更严格的回滚机制解析失败时自动调用 remove 逻辑确保不会留一个半吊子的状态在服务器上。6.5 常见问题速查表现象可能原因处理方式业务大量超时连接建立慢新建连接限速值低于真实基线调高NEW_CONN_RATE和NEW_CONN_BURST上传大文件返回 413request_body_max_size设得太小在配置片段中移除该行或调大到实际业务上限squid -k parse报错某个参数在旧版本不被支持按报错信息逐个注释不兼容参数只有 3128 受保护其他端口仍暴露Squid 配置了多个http_port脚本端口变量改为数组覆盖全部监听端口热加载后缓存命中率明显下降pipeline_max_prefetch 0影响预读确认业务对缓存命中率的要求必要时调回 2运维 IP 被限速挡在外面白名单没有包含当前出口 IP执行 apply 前先确认ADMIN_CIDR包含自己的管理网段重复执行 apply 导致重复规则脚本缺少幂等判断已加入前检测逻辑检测到标记或规则表则跳过7. 补丁之外这些加固动作也值得做7.1 升级计划不能真的往后无限拖缓解脚本做得再漂亮也只是权宜之计。它降低了漏洞被利用的窗口风险但没有修复漏洞本身。我建议在启用缓解脚本的同时就立刻启动升级评估流程把 Squid 从当前版本升级到官方发布的安全修复版本先在测试环境跑一轮核心业务回归再挑一个低峰窗口做灰度发布。升级完成后执行remove动作把脚本加的规则和配置片段全部撤掉恢复干净的运行状态。7.2 缩小暴露面比任何参数都重要涨经验之后我最大的体会是代理服务永远别直接暴露在公网上。如果业务必须对外提供服务前面一定要套一层云防火墙、负载均衡或安全组只放行来源可控的 IP 段。对仅内网使用的 Squid更简单粗暴的做法是在跑http_port的网卡选择上就直接绑定内网地址而不是0.0.0.0。端口暴露面的缩小往往比任何高危漏洞的缓解脚本都更有效因为它从根上让外部攻击者摸不到你的服务。7.3 安全脚本要纳入资产管理和演练计划最后再分享一个我在实际运维里的习惯像这种应急缓解脚本我从来不会让它只躺在某个服务器的/tmp目录里。我会把它放到配置管理仓库纳入版本管理注释里写清楚这个脚本是为了哪个 CVE、用了哪些参数、回滚命令是什么、负责人是谁。每半年再挑一个业务低峰期演练一次 apply 和 remove 全流程确保真出事的时候脚本能跑通、不会出现权限问题或过时参数。安全应急这件事准备的功夫大多花在平时。我自己在这套脚本上反复调整过几次最大的感想是写一个能在“环境复杂度高、业务不能停、时间窗口短”三重压力下稳定运行的脚本远比写一个功能齐全但只活在理想环境里的脚本有价值。它需要你理解网络层和应用层的配合逻辑也要你对被保护的 Squid 实例足够熟悉。如果你正在处理同样的漏洞告警希望这篇文章能帮你少走一些弯路把有限的精力留给真正重要的升级验证和业务保障。