我的博客

个人网站最容易被忽略的 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,及时更新存在漏洞的依赖。

这些措施都不复杂,花一个下午就能全部检查完。