做网站运维这些年我遇到最多的不是服务器突然宕机而是网站明明能开却慢得像幻灯片重启一下又正常过十分钟又卡死。这种时候很多人第一反应是机房线路问题其实大概率是被人打了应用层攻击——CC攻击。在Linux主机上判断CC攻击不需要装什么监控面板直接用系统自带命令几秒钟就能看出苗头方法非常直接有效。这篇文章就把我平时排查的完整思路和命令分享出来个人站长、中小公司运维、独立开发者只要你手里有一台Linux服务器这些命令就够用。先说明一点CC攻击和传统的带宽型DDoS不是一回事。DDoS是靠海量流量把带宽塞满而CC攻击是模拟真实用户发请求不挑大流量但能把Web服务进程、数据库连接池、CPU全部拖垮。你在带宽监控上看到流量可能只有几十兆但网站就是响应超时这就是CC最典型的特征。1. 先把CC攻击的原理和判断思路讲透1.1 CC攻击到底在打什么CC攻击全称是Challenge Collapsar本质上是应用层的DDoS变种。它对服务器资源的消耗方式可以拿饭店打比方一群人涌进餐厅不点菜只占座位每张桌都坐满真正来吃饭的客人在门口排队到天黑。服务器里的连接池、进程池、CPU资源就那么多被大量半死不活的请求占着正常用户访问就超级慢甚至连不上。具体到技术场景CC攻击通常瞄准三个层次Web服务器层疯狂请求某个动态页面或API接口让nginx代理或Apache进程满负荷运转。后端动态处理层大量请求触发PHP或者Python等后端程序执行php-fpm进程被耗光MySQL被链式拖垮。数据库层攻击慢查询接口让MySQL的CPU暴涨、连接数爆满整个数据库失去响应。理解这一层很重要因为你得先搞清楚攻击打的是Web层、后端层还是数据库层才能快速定位瓶颈。这决定了你后续要看什么命令的输出。1.2 判断CC攻击的总体思路我的排查思路非常简单就三步这三步互相印证缺一不可看连接状态用ss或netstat查看TCP连接数量与状态分布。连接数异常上涨是CC攻击的第一个信号。看系统资源用uptime查看负载、用top查看进程CPU占用。注意资源是否和连接数同步异常。看Web日志分析访问日志里的请求频率、单IP分布、请求路径。这是判断是否攻击的实锤证据。这三个维度单独看都可能骗人。比如高峰期连接数高是正常的爬虫抓取时单IP请求多也是正常的。只有连接数、负载、日志三者都出现异常同时指向同一个来源才能基本确认是CC攻击。我见过不少运维新手看到ESTABLISHED连接数到几千就慌了立刻拉黑IP结果把正常业务用户误杀了。所以先收集数据再下结论这个顺序不能乱。2. 网络连接状态命令一眼看出异常连接2.1 ss和netstat的用法与TCP状态含义现在的Linux发行版基本都标配iproute2工具包里面的ss命令是最推荐用的。它读取内核socket信息比老牌的netstat快得多在大连接数下也不会卡顿。用法上两者几乎一样ss -ant netstat -ant-a显示所有连接-n不做域名反解直接显示IP和端口-t只看TCP。输出结果里第一列是State也就是TCP状态。这一步主要是先掌握当前系统到底有哪几种连接状态数量分别是多少。TCP状态里和CC攻击直接相关的有这么几个状态含义CC攻击中的表现LISTEN服务正在监听端口正常就是nginx在等待连接ESTABLISHED已完成握手且正在通信的连接可能异常飙高表明大量“连接”正占用并保持资源SYN_RECV握手第二步服务器收到SYN但未完成异常增高表示可能有半连接冲击socket队列被塞满TIME_WAIT主动关闭连接后等待2MSL释放端口异常增多表示每秒新建大量连接快速建立又快速关闭CLOSE_WAIT对方关闭但本机程序未关闭socket如果持续暴增且不下降说明后端程序存在资源处理问题平时一个正常访问的网站ESTABLISHED可能就几百到一两千SYN_RECV和CLOSE_WAIT都很少见。CC攻击一打最直观的变化就是ESTABLISHED数量成倍上涨或者SYN_RECV、TIME_WAIT突然堆到几千。2.2 连接状态分布统计单看ss -ant的输出是几百行密密麻麻的眼睛会看花。效率最高的做法是把状态列提取出来计数ss -ant | awk NR1 {print $1} | sort | uniq -c | sort -rn拆解一下这条命令NR1是跳过第一行表头取第一列状态字段排个序再统计每种状态的出现次数最后按数量倒序排列。输出结果类似8237 ESTAB 412 TIME-WAIT 198 SYN-RECV 12 LISTEN 3 CLOSE-WAIT看到这种输出我心里基本就有数了。ESTAB占了8000多而机器平时的基线可能只有几百这就非常可疑。注意sort和uniq配合使用时sort必须排在uniq前面。uniq只能合并相邻的重复行不排序的话统计结果不准确。为了看变化趋势最好别只看一次快照。我常用watch命令每1秒刷一次watch -n 1 ss -ant | awk \NR1 {print $1}\ | sort | uniq -c | sort -rn或者更简单地循环执行while true; do ss -ant | grep -c ESTAB; sleep 1; done如果这个数字一直往上蹿不会回落那基本可以确定有外部的连接洪峰正在形成。2.3 单IP连接数排行状态统计确认连接总数异常后下一步是找出谁占用了这些连接。按远程IP分组统计连接数是锁定嫌疑IP的核心命令ss -ant | grep ESTAB | awk {print $5} | cut -d: -f1 | sort | uniq -c | sort -rn | head -n 20这条命令先筛出已建立的连接取第五列也就是远程地址和端口用cut把端口去掉只留IP再统计排序取前20名。正常网站的单IP连接数一般不会太高浏览器长连接也就几条到几十条。但如果某个IP的连接数到了几百甚至几千那这个IP有大概率是攻击源或代理节点。我自己在实战中见过单IP连接数达到2000多的这是非常明显的异常信号。命令里的列号不是完全固定的。新版ss输出是Local Address:Port和Peer Address:Port两列取第5列通常是远程IP。但不同发行版、不同版本可能有差异建议先跑一次ss -ant | head -5看表头和第5列到底是什么再决定用$5还是$6。还要注意IPv6的坑。cut -d: -f1只对IPv4地址有效IPv6地址里全是冒号切出来是错的。如果日志里IPv6来源较多可以换成awk -F {print $5}然后把地址里的端口去掉或者直接用sed s/\[.*//这类方式处理。大多数CC攻击场景还是IPv4为主但遇到IPv6地址你至少要知道这个坑。3. 系统资源命令从负载和进程找证据3.1 top定位CPU和进程异常连接数异常只是表象最终要确认的是资源被谁吃掉了。top是Linux运维最常用的动态资源查看命令输入top后按大写P按CPU排序按大写M按内存排序按大写C展开完整命令行按数字1展开每个CPU核心的使用率。正常Web服务器上nginxworker进程和php-fpm进程的CPU占用会有波动但总体稳定起伏不大。CC攻击时情况完全不同如果是打静态资源或动态页面nginx worker进程CPU占用会飙到接近100%并且多个worker同时拉满。如果是打PHP动态接口php-fpm进程数暴涨很多进程处于R运行状态CPU占用持续高位。如果是打数据库慢接口MySQL进程CPU会暴涨PHP进程反而在等待数据库返回处于S状态。top第一行的load average三个数字也要看。它们分别是过去1分钟、5分钟、15分钟的平均负载可以理解为“有多少任务在排队等待CPU”。判断标准很简单单核CPU满负载是1.0四核就是4.0。如果1分钟负载明显高于5分钟和15分钟说明负载是刚刚短时间内被拉起来的符合攻击突发的特征。CC攻击时的画面通常是负载从0.2一路涨到6.8nginx的CPU占用飙到300%以上多核累加整个服务器像被掐住脖子一样喘不上气。3.2 uptime、free、vmstat三件套top适合交互式观察但想快速记录和远程执行uptime更直接uptime输出里有当前时间、在线时长、登录用户数和负载均值。我一般先跑这一条几秒钟就能判断系统是否过载。内存层面用free -hfree -hCC攻击不一定吃内存但如果php-fpm进程被拖起很多内存会被大量蚕食最后触发swap换页进一步拖慢系统。free输出里available如果低于总内存的10%并且used快速上涨就说明资源正在被进程池耗尽。vmstat命令适合看连续采样的趋势vmstat 1 5每隔1秒采样一次共采样5次。重点看r列运行队列长度和us列用户态CPU时间占比。正常情况下r列应该在CPU核心数以内如果r长期大于核心数说明CPU已经排满了队机器的处理能力跟不上请求量。3.3 进程层面的攻击特征从进程层面判断CC攻击有几个常见特征nginx worker进程数量异常增多nginx的worker进程数一般由配置决定正常情况下数量稳定。如果某个瞬间worker进程数量翻倍说明有大量并发连接进入。php-fpm进程池被占满配置里max_children设了多少正常情况下不会全部被占满。CC攻击时每个请求都占用一个php-fpm进程池子瞬间耗尽新请求全部排队等待。MySQL慢查询日志暴涨攻击者专门挑慢查询接口打比如复杂的搜索、统计接口每个请求触发一次大查询数据库CPU先爆连带整个服务不可用。所以top看到的不一定是Web进程也可能是数据库进程。我之前遇到过一次nginx的CPU很正常负载却很高一排查发现是MySQL的CPU占用100%慢查询日志每秒钟刷几十条攻击目标是某个搜索接口每个请求都走一个巨复杂的SQL。这种情况如果只盯着nginx看半天找不到问题。4. Web日志分析锁定攻击频率与IP4.1 先知道日志记了什么连接和资源层面只告诉我们“系统有问题”但到底谁在打、打什么路径只有看访问日志才能实锤。nginx默认的access_log路径一般是/var/log/nginx/access.logApache的HTTPD默认是/var/log/httpd/access_log或者/var/log/apache2/access.log。日志的默认格式通常长这样1.2.3.4 - - [07/Oct/2024:15:04:32 0800] GET /api/search HTTP/1.1 200 5321 http://example.com/ Mozilla/5.0每一列的含义依次是客户端IP、用户标识通常为-、认证用户、请求时间、请求方法路径协议、状态码、返回字节数、Referer、User-Agent。后面分析单IP、请求路径、请求频率全靠这些字段。这里有个小经验生产环境建议把日志格式改成JSON格式用自定义log_format字段名一目了然后续用jq处理非常方便。但绝大多数人默认是combined格式下面的命令都按默认格式来写。4.2 单IP访问量排行判断CC攻击最直接的实锤就是统计某个时间段内每个IP的请求次数。如果某个IP在几分钟内产生了几千上万次请求基本可以说它不是正常用户。经典的一条命令awk {print $1} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 20awk提取每行第一个字段也就是客户端IP然后sort排序、uniq去重计数、sort再按数量倒序取前20名。输出结果类似32000 1.2.3.4 800 43.5.6.7 750 8.9.10.11如果日志已经做了切割归档要分析昨天的日志就换成对应的文件如果是.gz压缩包用zcat解压后管道给awkzcat /var/log/nginx/access.log.2.gz | awk {print $1} | sort | uniq -c | sort -rn | head -n 20只看全量日志有时会被积攒很久的数据干扰我通常只看最近一段时间的请求tail -n 10000 /var/log/nginx/access.log | awk {print $1} | sort | uniq -c | sort -rn | head -n 20这样统计的是最近1万条请求的来源分布更贴合当下是否正在被攻击的真实状态。4.3 请求路径和频率分析单IP请求多还不能完全定性因为搜索引擎爬虫也可能这样。要结合请求路径看。统计每个URL的请求次数awk {print $7} /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -n 20正常的网站请求路径分布会比较分散首页、栏目页、详情页、静态资源都有一部分。如果前几名里某一个API路径占了绝对大头比如/api/search一个路径占了总请求量的80%那这个接口就是攻击目标。再看请求频率也就是每秒产生多少请求。默认日志第4列是时间戳格式是[07/Oct/2024:15:04:32 0800]切出时间里的分和秒tail -n 5000 /var/log/nginx/access.log | awk {print $4} | cut -c14-18 | uniq -ccut -c14-18掐出15:04这样的分秒字段uniq -c按顺序统计每秒请求数。日志时间本身是有序的所以这条命令可以直接看出每秒请求量的连续变化。正常网站QPS到几十已经算不错了CC攻击时同一秒内可能出现上千条请求记录日志快速刷屏。我经常用tail -f直接盯日志tail -f /var/log/nginx/access.log如果屏幕以肉眼无法追踪的速度翻滚一行接一行全是同一个IP在请求同一个接口那攻击特征已经非常明显不需要再翻历史统计了。5. 综合判断与常见误判5.1 正常高并发 vs CC攻击很多人问高峰期连接数高、请求量大和CC攻击有什么区别两者从数据表象上看确实很像但有本质区别。这里我整理了一个对照表基本能帮你快速分类维度正常高并发CC攻击来源IP分散成千上万用户集中在少数几个IP或IP段请求路径分布自然符合业务逻辑集中在某个特定接口或页面请求频率波动平缓有规律突发性极强1秒内请求量暴涨User-Agent多样各类浏览器真实UAUA单一或明显异常甚至为空Referer有来源网页地址很多为空或固定一个异常来源行为特征有浏览、点击、停留节奏机械重复没有正常浏览路径比如一个做内容站的用户来访问通常是从首页进列表再点详情页停留一段时间再翻页。CC攻击的请求则是直接怼一个接口每秒几十上百发没有前导行为没有后续动作全是同一路径同一时间戳。5.2 一次完整排查实录说一个我印象很深的案例。某开发者运营一个咨询服务类网站平时日访问量也就两千多某天下午突然开始间歇性打不开。他跑来说“网站越到下午越慢重启一次管一会儿”。我登录服务器按顺序执行排查第一步跑uptime负载已经从平时0.2飙到6.84核CPU明显过载。第二步跑ss -ant的状态统计ESTAB连接数是平日的七八倍SYN_RECV也有几百明显异常。第三步跑top按CPU排序nginx worker进程CPU占用超过300%php-fpm进程数也异常膨胀说明有很多动态请求正在排队执行。第四步跑日志IP排行tail -n 5000 /var/log/nginx/access.log | awk {print $1} | sort | uniq -c | sort -rn | head -n 20前5个IP占了总请求量的80%头号嫌疑IP请求了接近两万次而正常用户一天也就几十次。第五步查这些请求打的是什么路径tail -n 5000 /var/log/nginx/access.log | awk {print $7} | sort | uniq -c | sort -rn | head -n 10结果几乎所有异常请求都集中在同一个搜索接口而其他路径的请求量占比极低。再翻看几个请求的用户代理UA是同一个固定值Referer为空。到这里结论已经非常清楚这是一个典型针对搜索接口的CC攻击。后续的应对是先在防火墙层临时封禁那几个高请求IP网站立刻恢复正常再观察日志确认没有新的异常IP冒出来最后给接口加上了Nginx层的速率限制。这个案例里每一步对应的都是前面说的命令从连接数到负载到进程再到日志一路追踪下来不存在“猜”的成分。5.3 命令组合速查表把整套排查浓缩成一张速查表方便你直接对照操作判断维度核心命令异常信号连接状态分布ss -ant | awk NR1 {print $1} | sort | uniq -c | sort -rnESTAB或SYN_RECV数量远超基线单IP连接数ss -ant | grep ESTAB | awk {print $5} | cut -d: -f1 | sort | uniq -c | sort -rn | head单个IP连接数达到几百上千系统负载uptime1分钟负载远高于5分钟和15分钟进程CPUtop按P排序nginx/php-fpm/mysql单进程CPU近100%单IP请求量tail -n 10000 access.log | awk {print $1} | sort | uniq -c | sort -rn | head单IP短时间内请求数千次请求路径tail -n 10000 access.log | awk {print $7} | sort | uniq -c | sort -rn | head请求集中在单一接口请求频率tail -n 5000 access.log | awk {print $4} | cut -c14-18 | uniq -c每秒请求数突然到几百上千这套命令组合在任何主流Linux发行版上都能直接跑不需要额外安装任何软件。6. 判断之后的应急处理建议6.1 临时封禁要快更要准确认攻击IP之后最快的止损方式就是直接在防火墙层封掉。用iptables一条命令就行iptables -I INPUT -s 1.2.3.4 -j DROP封禁后网站一般会立刻恢复。但有两个注意事项。第一封之前要确认这个IP不是CDN节点、反向代理或者负载均衡器的出口IP。很多站点用了CDN加速访问日志里看到的IP可能是CDN的节点IP而不是真实用户IP。如果把CDN节点封了全站用户都进不来比被攻击还惨。判断方法很简单看这个IP的访问是否覆盖了大量不同来源和时间是否带上了CDN回源请求的特征。拿不准的时候可以先不封只做限速宁可慢一点也别误杀。第二直接iptables封禁只对当前生效系统重启或者iptables服务重启后规则就丢了。如果是临时应急没问题如果要持久化得把规则保存到/etc/iptables/rules.v4或者用firewalld对应的持久化命令。个人建议应急阶段先手动封等攻击结束后再统一清理避免留下太多长期失效规则。6.2 Nginx层接口限流临时封IP治标不治本因为攻击者可以换IP继续打。更稳健的做法是在Web服务层面加限流让单个IP的请求频率限制在合理区间。nginx的limit_req模块是官方自带的用法不复杂。以限制某个接口的请求速率为例在http块里定义一个限流区域limit_req_zone $binary_remote_addr zoneapi_limit:10m rate10r/s;然后在对应的location里引用location /api/ { limit_req zoneapi_limit burst20 nodelay; }这段配置的意思是每个IP访问/api/下接口的速率限制为每秒10个请求允许瞬时突发20个超出的直接返回503。zone后面定义的内存空间大小10MB可以存储大约16万个IP状态个人站点完全够用。关于burst和nodelay的参数burst20相当于一个缓冲队列允许短时间内的请求积压一部分再处理适合有突发特征的正常业务nodelay表示超出速率和队列的请求直接拒绝不等待延时处理。对于抵抗CC攻击nodelay很有效果但对业务容忍度很低的接口需要根据实际场景调参数别一上来就10r/s压到用户正常操作都做不了。6.3 长期防护建议限流是应急手段长期来看还有几件事值得做接入WAF或CDN清洗能力。大部分云厂商的WAF都有CC防护规则能够识别高频请求并自动拦截。有预算的站点直接上云WAF最省心。接口层做鉴权和频率限制。业务层面可以用代码实现验证码、登录限次、接口鉴权让攻击者无法用未登录状态无限请求关键接口。日志监控与告警。把ss连接数、系统负载、每秒请求量这些指标接到监控系统里设置阈值告警。攻击刚开始就能收到通知而不是等用户投诉才发现。数据库层面做防护。慢查询接口是大目标给核心查询加缓存、加索引让接口本身变得“耐打”即使被打也不会轻易拖垮数据库。没有绝对安全的系统但把应急响应流程练熟了攻击来了至少不会手忙脚乱。7. 常见问题与排查技巧实录7.1 连接数高但CPU不高怎么解释有读者问过ss显示连接数有7000多但top里CPU很闲负载也不高这是不是被CC了这种情况确实存在但原因往往是攻击没有真正到达应用层。比如大量SYN_RECV堆积服务器完成了TCP握手的一部分但没完成全部请求根本没到nginxCPU自然不高。这更像是传统的SYN洪水属于网络层DDoS的一种。观察特征是SYN_RECV数量大而ESTABLISHED不多。另一种可能是大量连接处于TIME_WAIT状态。如果攻击工具建立连接后瞬间关闭产生了海量TIME_WAIT但每次连接持续的时间极短应用层也没有实际处理多少请求CPU也不会高。这时候要看的是连接是否在被疯狂地“建立-关闭-建立-关闭”以及日志里请求量是否真的上去了。没有日志支撑的连接数异常优先考虑网络层洪泛而不是CC。7.2 如何正确识别搜索引擎爬虫单IP请求排行里排第一的是爬虫这种情况非常普遍不能一刀全封。分辨爬虫主要看几个特征日志里的User-Agent字段是否带有爬虫标识比如常见的搜索引擎爬虫UA都有固定格式域名里也常带“bot”“spider”“crawl”这些字样。请求路径是否有规律爬虫倾向于按目录层级遍历抓取先抓首页再抓栏目页有比较明显的广度和深度特征而不是只盯着一个接口高频打。访问时间分布是否均匀爬虫是长期抓取频率相对稳定白天黑夜都有CC攻击通常是某段时间突然爆发来得快走得也快。请求是否遵循robots协议正规爬虫会定期抓取robots.txt而这个文件在正常用户行为里基本不会被访问。如果确认是爬虫又不想让它太消耗资源正确的做法是对爬虫做限速或用robots协议约束不要直接封禁。误封搜索引擎爬虫会导致收录和排名下降这个代价比被攻击还难受。7.3 日志里有大量不存在的路径请求有时候日志里出现大量404请求的路径在站点里根本不存在比如/wp-admin、/admin.php、/.env这些或者一串随机字符拼起来的路径。这类请求多数是扫描器在探测漏洞不一定是CC攻击。判断方法还是看频率和数量级。如果404请求只占一小部分来源IP也分散那只是常规扫描噪声不用管。如果某个IP对不存在的高频请求占了日志的大头而且持续刷不正常的时间那就可以视为恶意扫描。处理方式同样是确认IP后封禁同时检查一下站点有没有明显的漏洞入口尤其是后台登录接口、文件上传接口这类敏感路径确认没被打穿。7.4 动态观察命令的小技巧排查CC攻击时最忌讳看一次快照就下结论。我给你一个组合技巧把连接数和日志刷新放到同一个屏幕上动态看watch -n 2 ss -ant | grep ESTAB | wc -l; tail -n 1 /var/log/nginx/access.logwatch每两秒刷新一次第一行输出当前ESTAB连接数第二行输出日志里最新一条记录。屏幕刷新时如果连接数一路攀升日志也不断有新内容吐出来你就知道攻击正在进行中。做多台服务器的排查时不要一台一台上去敲命令。我习惯把关键命令写成一个脚本循环跑所有服务器把输出汇总后对比一眼看出哪台机器连接数异常。比如for host in server1 server2 server3; do ssh $host echo $host: \$(ss -ant | grep -c ESTAB) / load: \$(uptime | awk -Fload average: {print \$2}) done大概是这个思路具体主机列表和密钥配置按自己的环境调整。这样能快速定位是哪台机器接敌而不是一登录就深陷在其中一台里。另外强烈建议你在系统正常的时候记录一份“基线数据”。比如用同样的统计命令记下平时ESTAB连接数的大概范围、负载均值、单IP连接数Top5、每秒请求量。把这些存到一个文本文件里甚至写进监控脚本。一旦出问题拿当前数据和基线一对比异常还是正常一目了然排查速度和准确性都会明显提升。我踩过最深的坑就是没有基线数据高峰期连接数翻了一倍就误判成攻击导致误封了一些真实用户。后来学乖了每次部署完新环境先跑一套统计存起来真出事的时候这就是最靠谱的参照物。最后再提醒一件事CC攻击的判断本质是“数据异常特征匹配”。连接数、负载、日志三条线同时验证结论才站得住脚。考虑到攻击手法也在不断变化今天打接口、明天打下载、后天打登录但不管怎么变三条线的排查逻辑是不变的。把这个流程内化成肌肉记忆再遇到网站突然变慢几分钟就能有结论。