安全加固
上线前 10 件必做的事、密钥与权限的正确姿势、注入/XSS/CSRF 的边界,以及被挂马或被 SEO 劫持后怎么查。
约 13 分钟
进阶
代码块右上角可一键复制
本页目录(13 分钟读完)
这一章只做一件事:让站点不被人端掉。
99% 的被挂马不是框架漏洞。
是弱密码、目录权限过宽、插件来路不明、备份没做。伪静态在 建站第一小时。 本章只讲安全规则,不掺伪静态。
上线前必做这 10 件#
作用:堵住最常见的入口。
步骤:逐条做完。
| 顺序 | 做什么 | 怎么做 |
|---|---|---|
| 1 | 删掉 install/ 目录 | mv install /root/backup |
| 2 | 确认 install.lock 存在 | 看 runtime/install.lock |
| 3 | 后台密码改强密码 | 16 位以上,别和邮箱同密码 |
| 4 | 开登录验证码 | 系统设置 → 安全设置 |
| 5 | 后台绑定 IP | 有固定 IP 才填 |
| 6 | 数据库最小权限 | 别用 root 连站 |
| 7 | data/ 禁止 Web 访问 | 见下面「安全规则」 |
| 8 | 备份 secret.key | 见下面「怎么管密钥」 |
| 9 | 全站 HTTPS | 证书配好 + 开 HSTS |
| 10 | 配好备份并验证 | 能恢复才算数 |
打不开? → 看 排错手册
怎么配安全规则#
作用:禁止直接访问核心目录。和伪静态无关,单独加。
步骤:
- 打开 Nginx 站点配置(宝塔:网站 → 设置 → 配置文件)
- 在
server { }里加上下面这些行 - 保存,重载 Nginx
# 安全规则:禁止直接访问核心目录
location ^~ /app/ { deny all; }
location ^~ /core/ { deny all; }
location ^~ /config/ { deny all; }
location ^~ /data/ { deny all; }
location ^~ /docs/ { deny all; }
location ^~ /lang/ { deny all; }
location ^~ /tests/ { deny all; }也可以直接用随包自带的完整规则:.workbuddy/deploy/00-security.conf。
整个文件放进宝塔的「网站 → 设置 → 配置文件」里。
效果:
| 访问 | 结果 |
|---|---|
/data/secret.key | 403 |
/config/app.php | 403 |
/core/Bootstrap.php | 403 |
验证:
curl -s -o /dev/null -w "%{http_code}\n" https://你的域名/data/secret.key
# 期望 403 或 404,绝不能是 200这些规则只管「谁能直接访问文件」。
伪静态是另一回事,在建站第一小时里配。
不要把两者混在一起写。打不开? → 看 排错手册
怎么改后台路径#
作用:让别人猜不到你的后台地址。
步骤:
- 打开
config/app.php - 找到
admin_path - 改成猜不到的名字
'admin_path' => 'my-console-9f2',- 重建缓存
php aucms cache:rebuild- 检查 Nginx 里有没有硬编码
/admin - 确认没有插件把
/admin写死
效果:/admin 打不开,要用新路径进后台。
打不开? → 看 排错手册
怎么管密钥#
作用:data/secret.key 决定全站加密安全。
| 事实 | 说明 |
|---|---|
| 唯一来源 | data/secret.key,安装时生成 |
| 配置里必须为 null | 不要写死值 |
| 读不到就失败 | 不会降级成不加密 |
| 不能进版本库 | 也不该在发布包里 |
备份步骤:
- 复制到安全地方
cp data/secret.key ~/aucms-secret-backup-$(date +%F).key- 放进密码管理器或离线备份
- 不要放在网站目录里
丢了会怎样:
| 影响 | 说明 |
|---|---|
| 加密配置解不开 | 如第三方支付 secret |
| 签名链接全部失效 | 下载、激活、找回密码 |
| 已登录用户被踢下线 | — |
丢了没有「找回」。
只能换新密钥 + 重新配三方凭证。
所以备份它比备份数据库还优先。打不开? → 看 排错手册
怎么配权限#
作用:控制谁能进后台、谁能点什么。
三层防线:
| 层 | 管什么 |
|---|---|
| 会话层 | 能不能进后台(登录 + 指纹 + IP + 超时) |
| 操作层 | 敏感操作只有超管能做 |
| 数据层 | 普通管理员能看哪些模块 |
给普通管理员授权:
- 后台 → 插件 → 会员管理 → 角色与权限
- 建角色,勾权限点
- 会员详情里把人放进角色
没声明过的模块,默认只有超管能进。
这是「默认拒绝」,不是 bug。
新增插件后台页时记得注册权限点。写敏感操作时注意:
$deny = $this->requireSuperAdmin();
if ($deny !== true) {
return $deny; // 返回的是 403 响应,不是 false
}requireSuperAdmin() 返回 true 或 Response,不是 true/false。
写成 if (!$this->requireSuperAdmin()) 会把 Response 当 false,
表现为「点了没反应」。打不开? → 看 排错手册
怎么防 SQL 注入#
作用:别让攻击者拼 SQL。
规则只有一条:所有用户输入都通过 QueryBuilder 传参。
正确写法:
$db->table('content')->where('id', $id)->where('status', 1)->first();
$db->table('content')
->where('title', 'like', '%' . $kw . '%')
->get();危险写法:
$db->query("select * from content where id = " . $_GET['id']);whereRaw() / select() 里的字符串不经过参数绑定,必须自己保证安全。
config/security.php 里 allow_raw_sql 默认 false —— 保持默认。怎么防 XSS#
作用:别让攻击者注入脚本。
| 场景 | 做法 |
|---|---|
| 标题、昵称、摘要 | 模板默认转义,什么都不用做 |
| 富文本正文 | 入库前 Security::purify(),输出 |raw |
| 用户提交的富文本 | 先 purify() 再存,绝不能直接 |raw |
系统已内置(了解即可):
$row['content'] = \Aucms\Security::purify($input['content']);
$row['title'] = \Aucms\Security::strip($input['title'], 255);purify() 会去掉 <script>、<iframe>、on* 属性、javascript: 链接。
正文要嵌视频得改 xss 白名单 —— 改之前想清楚风险。怎么防 CSRF#
作用:别让别的网站伪造你的表单提交。
两个令牌,都要有:
| 令牌 | 字段名 | 防什么 | 有效期 |
|---|---|---|---|
| CSRF Token | _token | 跨站伪造 | 会话级 |
| 表单 Token | _form_token | 重复提交 | 一次性 |
表单里这么写:
<form method="post" action="/admin/xxx/save">
<input type="hidden" name="_token" value="{$csrf}">
<input type="hidden" name="_form_token" value="{$form_token}">
...
</form>少了会怎样:
| 少了什么 | 提示 |
|---|---|
_token | 419 跨站请求被拒 |
_form_token | 419 表单已过期 |
| 两个都少 | 419,信息可能不一样 |
打不开? → 看 排错手册
上传安全:系统已挡的三件事#
| 挡了什么 | 怎么挡的 |
|---|---|
| 伪装后缀 | 文件名重命名,按真实类型定扩展名 |
| 上传目录执行脚本 | Nginx 规则禁止(要用 00-security.conf) |
| 危险扩展名 | php/phtml/phar/svg/html 强制剔除 |
验证上传目录不能执行:
curl -s -o /dev/null -w "%{http_code}\n" https://你的域名/runtime/upload/test.php
# 期望 403被挂马了怎么查#
作用:发现异常时按顺序排查。
五个症状:
| 症状 | 可能是 |
|---|---|
| 从搜索点进来跳赌博站 | SEO 劫持 |
| 页面底部多出隐藏链接 | 黑链 |
| 网站变慢、CPU 跑满 | 挖矿脚本 |
| 后台多出陌生账号 | 被种后门 |
| 手机跳转、电脑正常 | UA 判断型木马 |
排查顺序:
| 顺序 | 查什么 | 命令 |
|---|---|---|
| 1 | 入口文件有没有被动 | md5sum index.php 对比官方包 |
| 2 | 最近改过的 PHP | find . -name "*.php" -mtime -7 |
| 3 | 上传目录有没有可执行文件 | find runtime/upload -name "*.php" |
| 4 | 可疑函数 | grep -rn "eval|base64_decode" --include="*.php" |
| 5 | 异常计划任务 | crontab -l |
| 6 | 隐藏文件 | find . -name ".*.php" |
SEO 劫持专项:
用蜘蛛 UA 访问,和正常 UA 对比:
curl -s -A "Mozilla/5.0 (compatible; Baiduspider/2.0)" https://你的域名/ | tail -40
curl -s https://你的域名/ | tail -40两边 HTML 不一样,就是有 UA 判断。常见藏身处:
| 藏身处 | 怎么查 |
|---|---|
index.php 顶部 | 对比官方包 |
.user.ini 的 auto_prepend_file | 看文件内容 |
| nginx 的 sub_filter | 看配置 |
主题 footer.html | 看有没有插 JS |
| 来路不明的插件 | 逐个停用试 |
清理步骤:
- 备份现场(整个目录 + 数据库),别急着删
- 用官方包覆盖被改的文件
- 改所有密码:后台、数据库、FTP、SSH、宝塔
- 检查有没有新增管理员、新增计划任务
- 检查 nginx 有没有被加 sub_filter
- 升级到最新版本
- 观察 3 天日志
只删木马不改密码 = 没修。
二次入侵大多从旧密码和留着的后门账号进来。打不开? → 看 排错手册
怎么看日志追责#
| 日志 | 在哪 | 看什么 |
|---|---|---|
| 应用日志 | runtime/log/Y-m-d.log | 异常堆栈 |
| 后台操作日志 | 后台 → 操作日志 | 谁删了什么 |
| 登录日志 | 会员详情页 | 异常 IP |
| Web 访问日志 | Nginx 站点日志 | 高频 404、可疑 UA |
找出可疑 IP:
# 访问量异常高的
awk '{print $1}' /www/wwwlogs/站点.log | sort | uniq -c | sort -rn | head -10
# 扫描器(大量 404)
grep " 404 " /www/wwwlogs/站点.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10主机层:最后一道门#
| 检查项 | 命令 | 期望 |
|---|---|---|
| 只开该开的端口 | ufw status verbose | 没有多余端口 |
| 数据库不监听公网 | grep bind-address /etc/mysql/my.cnf | 127.0.0.1 |
| MySQL 没有 % 账号 | mysql -e "select user,host from mysql.user where host='%';" | 空 |
宝塔、SSH、FTP 全部改非默认端口 + IP 白名单。
这三样是服务器被拿下的主入口,比 CMS 本身风险大得多。改完代码先跑这三条#
php .workbuddy/tools/lint_classes.php
php .workbuddy/tools/lint_methods.php
php .workbuddy/tools/lint_redirects.php出事了怎么走#
| 看到什么 | 先做什么 |
|---|---|
| 打不开 | 看 runtime/log + server_debug.php |
| 被跳转 | 用蜘蛛 UA 对比 HTML |
| 变慢了 | 看 CPU/带宽,查挖矿脚本 |
| 数据被改 | 看后台操作日志定位账号 |
然后:
- 备份现场(目录 + 数据库 + 日志)
- 断源(改密码 / 关站点 / 关插件)
- 用官方包覆盖未知文件
- 复现验证 + 观察日志
- 写复盘:入口、时间、影响、怎么防
还没解决?→ 看 排错手册
接下看哪一章#
没找到答案?
在后台「缓存与维护 → 系统体检」里看环境是否正常;仍无解就打开
server_debug.php 复现一次,页面会直接告诉你异常文件与行号。