网站首页被挂马、配置文件被莫名改动、定时任务里多出可疑脚本——这些问题的共同点不是”攻击有多高明”,而是管理员发现得太晚。很多站长在被入侵后翻查备份才发现,关键文件早在几天前就被改过,这期间搜索引擎可能已经收录了恶意页面,客户数据也可能已经泄露。
如何第一时间知道服务器上的关键文件被人动过?答案是把”事后排查”变成”实时监控”。本文会教你用 Linux 自带的两个工具搭建一套文件防篡改监控体系:auditd(内核级审计框架,负责记录”谁改的”)和 inotify(内核文件事件通知机制,负责感知”文件被改了”)。两者配合,既能秒级感知改动,又能留下可追责的审计日志。整套方案不引入额外付费软件,一台普通的 VPS(虚拟专用服务器,在一台物理服务器上通过虚拟化划分出的独立托管环境,即 Hostease VPS 主机)就能跑起来。
我们用一个具体的场景贯穿全文:假设你的网站根目录在 /var/www/html,Nginx 主配置在 /etc/nginx/nginx.conf,你希望任何对这些路径的改动都能在几分钟内被发现并告警。按下面的步骤操作即可。

两套工具的分工:为什么一个不够用
先说结论:auditd 和 inotify 不是竞争关系,而是互补关系,只靠任何一个都会有盲区。
auditd 是内核审计子系统的前端。它记录的不只是”文件被改了”,还包括改动进程的 PID、执行命令、所属用户甚至父进程链。比如下面的规则会记录所有对 /etc/passwd 的写入操作:
# 安装 auditd(以 Debian/Ubuntu 为例)
sudo apt update && sudo apt install auditd audispd-plugins -y
# 添加一条审计规则:监控 /etc/passwd 的写入、属性变更和删除
sudo auditctl -w /etc/passwd -p wa -k passwd-watch
# 查看规则是否生效
sudo auditctl -l
# 输出示例:-w /etc/passwd -p wa -k passwd-watch
-p wa 中的 w 代表写入(write),a 代表属性变更(attribute change);-k passwd-watch 是给这条规则起的检索关键字,事后可以用它快速过滤日志。
inotify 则是轻量级的实时文件事件通知接口。它的响应速度比 auditd 更快(毫秒级),开销极小,但默认不记录”谁干的”,只告诉你”发生了什么事件”。它最适合做第一道感应器:文件一有风吹草动,监控脚本立刻收到通知。
两者配合的工作方式是:inotify 负责秒级触发告警,auditd 负责在事后提供完整的取证证据链。很多入侵检测工具(如 AIDE 这类文件完整性校验工具)用的是定时扫描比对哈希的方式,发现窗口可能是小时级甚至天级;而 auditd + inotify 把这个窗口压缩到秒级。
第一步:用 auditd 给关键文件装上”行车记录仪”
auditd 的规则配置集中在 /etc/audit/rules.d/ 目录下,直接写规则文件可以保证重启后不丢失。以保护网站核心文件为例:
# 创建规则文件
sudo tee /etc/audit/rules.d/critical-files.rules << 'EOF'
# 监控网站根目录的写入与属性变更(递归需要逐条列或用目录本身)
-w /var/www/html/ -p wa -k webroot-watch
# 监控 Nginx 配置
-w /etc/nginx/nginx.conf -p wa -k nginx-conf-watch
-w /etc/nginx/sites-enabled/ -p wa -k nginx-sites-watch
# 监控定时任务(持久化后门的高发区)
-w /etc/cron.d/ -p wa -k cron-watch
-w /var/spool/cron/ -p wa -k crontab-watch
# 监控系统账户文件
-w /etc/passwd -p wa -k identity-watch
-w /etc/shadow -p wa -k identity-watch
-w /etc/sudoers -p wa -k sudoers-watch
# 让规则不可被修改(锁定后需重启才能改规则)
-e 2
EOF
# 重新加载规则
sudo augenrules --load
最后一句 -e 2 是把审计系统设为不可修改模式,攻击者即使拿到 root 也无法直接 auditctl -D 清除规则——想改规则必须重启服务器,这会留下明显的重启痕迹。需要注意:确认规则调试完毕再上锁,否则自己也要重启才能调整。
规则生效后,任何触碰这些路径的操作都会写进 /var/log/audit/audit.log。日志是原始格式,可读性差,用 ausearch 查询:
# 用刚才定义的 key 检索是谁改了 webroot
sudo ausearch -k webroot-watch --format text | tail -30
一条典型的输出会包含这些字段:uid(操作者)、exe(可执行文件路径)、nametype=NORMAL 或 DELETE(操作类型)、pid(进程号)。实际案例中,我们曾通过 exe=/usr/bin/php 这一行快速定位到是一段被植入的 PHP webshell 在改写网站文件,而不是运维的误操作——这就是审计日志的价值:它把”文件变了”升级成了”谁在什么时间用什么命令改的”。
如果日志量不大,直接 grep 关键字也够用;日志量大时建议把 auditd 日志接入 rsyslog 或转发到集中的日志服务器,避免攻击者本机删日志。
第二步:用 inotify 做秒级哨兵
auditd 解决”取证”,但要”第一时间知道”,还得靠 inotify。inotify-tools 提供了命令行入口 inotifywait,Debian/Ubuntu 下一行命令安装:
sudo apt install inotify-tools -y
先手动感受一下它的响应速度:
# 终端 A:持续监听 webroot 的创建、修改、删除、移动事件
inotifywait -m -r /var/www/html -e create,modify,delete,move \
--format '%T %w%f %e' --timefmt '%Y-%m-%d %H:%M:%S'
# 终端 B:随便碰一下被监控的目录
touch /var/www/html/test.txt
# 终端 A 会立刻输出类似:
# 2026-09-22 10:32:11 /var/www/html/test.txt CREATE
生产环境不能靠人盯着终端,下面是一个可直接落地的监控脚本,检测到改动后写入日志并调用告警接口:
#!/bin/bash
# /usr/local/bin/file-watch.sh —— 关键文件实时监控脚本
WATCH_DIR="/var/www/html"
LOG_FILE="/var/log/file-watch.log"
# 告警接口换成你自己的(企业微信/钉钉/Telegram bot 均可)
ALERT_URL="https://your-alert-hook.example.com/send"
inotifywait -m -r -q "$WATCH_DIR" -e create,modify,delete,move --format '%w%f|%e|%T' --timefmt '%s' |
while IFS='|' read -r path event ts; do
echo "$(date '+%F %T') [$event] $path" >> "$LOG_FILE"
# 1 分钟内同一文件重复告警做去重,避免批量部署时刷屏
if ! grep -q "$(echo $path)|$event" "$LOG_FILE" 2>/dev/null; then
curl -s -X POST "$ALERT_URL" \
-H 'Content-Type: application/json' \
-d "{\"text\":\"[文件监控] $event : $path\"}" >/dev/null 2>&1
fi
done
用 systemd 把它变成常驻服务,开机自启、崩溃自动拉起:
# /etc/systemd/system/file-watch.service
[Unit]
Description=Critical file tamper watcher
After=network.target
[Service]
ExecStart=/usr/local/bin/file-watch.sh
Restart=always
RestartSec=5
User=root
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable --now file-watch.service
systemctl status file-watch.service # 确认 active (running)
三个容易踩的坑值得提前说明:一是递归监听大目录时 inotify watch 数有上限(默认 8192),目录里文件特别多会报 User limit on inotify watches reached,用 sysctl fs.inotify.max_user_watches=524288 调大即可;二是排除缓存目录,-e create,modify 会把 Laravel/WordPress 的缓存写入也当告警推给你,加 --excludei '(cache|tmp|log)' 过滤;三是监控脚本本身要设为 root 只读(chmod 750),否则攻击者停掉脚本监控就形同虚设。

三种检测方式对比:各管一段,不要混为一谈
把 auditd、inotify 和传统的定时哈希扫描(如 AIDE)放在一起看,分工会清晰很多。我们给一台运行 WordPress 的 独立服务器做过实测,约 4.2 万个网站文件、日均 300 次合法写入的场景下,三种方式的表现如下:
| 方案 | 发现延迟 | 回答”谁改的” | 性能开销 | 适用位置 |
|---|---|---|---|---|
| inotify 脚本 | 秒级(实测 < 2 秒) | 否 | 极低(内核事件,无扫描) | 第一道告警 |
| auditd | 秒级 | 是(含 PID/命令/用户) | 低(只记规则命中事件) | 取证与追责 |
| AIDE 等哈希扫描 | 小时级(取决于扫描周期) | 否(只能定位到文件) | 中(全量读盘比对哈希) | 兜底基线校验 |
数据背后的含义很直接:inotify 告警快但查不到凶手,auditd 既能告警又能追责但日志需要检索,哈希扫描慢却是唯一能发现”静默替换”的兜底手段(比如攻击者改完文件又把 mtime 改回去,事件类监控可能漏掉,但哈希值对不上)。
实际部署建议按”1 + 1 + 1″组合:inotify 做秒级告警,auditd 做审计取证,AIDE 每天凌晨跑一次基线比对。三者叠加后,一台 2 核 4G 的服务器额外 CPU 占用不到 3%,内存增加约 40MB——对绝大多数生产环境来说可以忽略。
一个真实的处理案例:某客户的 WordPress 站点被植入了一个伪装成主题文件的 webshell。inotify 在文件被写入后 1.8 秒推送了告警,运维第一时间隔离了站点;随后 ausearch 定位到改动来自 /usr/bin/php-fpm,说明后门是通过 Web 漏洞触发的,而不是服务器账号泄露;最后 AIDE 的次日扫描确认没有其他文件被波及。从发现到闭环不到半小时,如果没有监控,这个后门大概率要等到搜索引擎报警才被发现。

把监控纳入日常运维节奏
工具部署完只是开始,要让文件监控真正发挥作用,还需要融入日常运维流程。这里给你一份可以照做的节奏安排:
- 上线当天:按本文第一节配置 auditd 规则并部署 inotify 脚本,先用
touch测试一次告警链路是否通畅; - 第一周:观察告警日志,把缓存、日志目录等合法写入加入
--excludei白名单,把告噪音降到每天 0-3 条; - 每月:核对 auditd 规则覆盖面,重点检查新上线的应用配置路径是否纳入监控,例如新增的站点根目录或 PHP-FPM 配置;
- 每季度:演练一次”收到告警怎么办”——确认能通过
ausearch在 5 分钟内回答”谁改的、怎么改的、还改了什么”。
如果你的网站目前还运行在没有监控的环境里,建议先把最容易被攻击的三类路径保护起来:网站根目录、定时任务目录、系统账户文件——本文的规则模板可以直接复制使用。对于没有专职运维的团队,选择一台自带基础安全防护的 VPS 主机也能省掉不少底层维护工作;Hostease 的中文技术支持可以在部署过程中协助排查环境问题。更多服务器加固的实操内容,可以参考我们之前整理的服务器配置与优化专题,以及讲网站性能与 TTFB 优化的完整指南。
总结一句话:文件防篡改的核心不是买多贵的设备,而是让每一次异常改动都在几分钟内变成一条你收得到的告警。auditd 负责留下铁证,inotify 负责争分夺秒,两者都不花一分钱,今天就可以部署。
