个人网站最容易被忽略的 6 个安全问题
个人网站被攻击,很少是因为什么高深的漏洞,大多数是一些低级失误被自动化扫描器发现了。把服务器的访问日志翻出来看看,你会发现每天都有大量请求在试探 /.env、/.git/config、/wp-admin 这些路径。
下面这 6 个问题,建议逐个检查一遍。
1. .env 文件能被直接下载
如果把整个项目目录设置成了 Nginx 的静态根目录,.env 里的数据库密码、支付密钥就能被直接下载。
检查方法:
curl -I https://你的域名/.env
如果返回 200,立刻处理。正确的做法是静态目录只指向 public/ 这样的专用目录,同时在 Nginx 中禁止访问隐藏文件:
location ~ /\. {
deny all;
}
如果确认已经泄露,必须更换所有密钥,仅仅删掉文件是不够的。
2. .git 目录暴露
用 git clone 部署到服务器,又把项目目录直接作为网站根目录,.git 目录就会暴露。攻击者可以借此还原出完整的源代码,包括历史提交里曾经出现过的密码。
上面那条 Nginx 规则同样能解决这个问题。
3. 后台弱密码
admin/admin123、admin/123456 是扫描器必试的组合。后台建议:
- 使用足够长的随机密码;
- 增加登录失败次数限制;
- 有条件的话,后台地址不要用
/admin这种常见路径,或者限制只允许特定 IP 访问。
4. SQL 拼接
// 危险
db.prepare(`SELECT * FROM users WHERE name = '${req.query.name}'`).get();
// 正确
db.prepare('SELECT * FROM users WHERE name = ?').get(req.query.name);
只要用了参数化查询,SQL 注入基本就不会发生。需要特别注意的是 ORDER BY、表名这类不能使用占位符的地方,应该用白名单校验。
5. 用户输入直接输出到页面
用户名、评论、搜索关键词,如果不经转义直接输出到 HTML,就会产生 XSS 漏洞。
- 模板引擎默认的输出方式通常会自动转义(EJS 的
<%= %>),不转义的输出(EJS 的<%- %>)只用于自己可控的内容; - 允许用户提交 Markdown 的场景,渲染后的 HTML 必须经过白名单过滤,因为 Markdown 里可以直接写 HTML。
6. 报错信息暴露给用户
开发环境下详细的错误堆栈很方便,但在生产环境中,它会暴露文件路径、依赖版本甚至 SQL 语句。
- 生产环境设置
NODE_ENV=production; - 错误处理中间件只返回通用的提示信息,详细信息写入日志。
顺手做的几件事
- 去掉
X-Powered-By响应头(Express 中使用app.disable('x-powered-by')); - Cookie 设置
HttpOnly、SameSite=Lax,HTTPS 站点加上Secure; - 定期执行
npm audit,及时更新存在漏洞的依赖。
这些措施都不复杂,花一个下午就能全部检查完。