给内部系统的 Nginx 再加一道门:Basic Auth 配置记录

2026年07月29日9 次阅读0 人喜欢
nginxNginx安全认证系统反向代理配置
所属合集

最近有个内部系统需要放到公网入口上,但它本身已经有一套登录。最开始我觉得这就够了,后来还是想在 Nginx 前面再加一层 Basic Auth。

原因不完全是担心有人猜到应用账号密码。更现实一点:只要登录页能直接打开,扫描器就能拿到网页标题、Logo、前端静态资源、接口路径,甚至从报错和构建产物里猜出技术栈。对于只给内部人员用的系统,这些内容其实没必要先暴露出去。

所以最后的思路是:先让 Nginx 拦住入口,过了 Basic Auth 才能看到应用自己的登录页;进入应用后,还是照常走原来的账号、权限和验证码。两层认证解决的不是同一个问题。

配置

这个系统是一个前端单页应用,构建结果在 /home/position-tree/dist,Nginx 配置最后是这样的:

nginx 复制代码
server {
    listen 82;

    root /home/position-tree/dist;
    index index.html;

    auth_basic "仅限内部许可访问";
    auth_basic_user_file /etc/nginx/.htpasswd;

    location / {
        try_files $uri $uri/ /index.html;
    }
}

auth_basic 打开 HTTP Basic Authentication。后面的文字是认证域(realm),浏览器弹出用户名密码框时会显示它。

auth_basic_user_file 指向用户文件。这个文件保存的是用户名和密码哈希,Nginx 每次收到请求时都用它校验请求里的凭据。

配置放在 server 级别比较省心:首页、Logo、JS、CSS 和通过 Nginx 暴露出来的其他路径都会先经过认证。这样未认证请求拿不到 index.html,也就拿不到网页标题和页面内容。

try_files $uri $uri/ /index.html; 和认证没有直接关系,它只是 SPA 的路由回退。访问一个没有对应真实文件的前端路由时,Nginx 最终还是返回 index.html,交给前端路由处理。

浏览器到底发了什么

第一次访问受保护路径时,浏览器还没有认证信息。Nginx 不会把页面返回给它,而是返回:

http 复制代码
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Basic realm="仅限内部许可访问"

浏览器识别到 WWW-Authenticate 后,就会弹出原生的用户名密码输入框。

用户输入凭据后,浏览器会在后续请求中带上:

http 复制代码
Authorization: Basic YWRtaW46c2VjcmV0

这里的 YWRtaW46c2VjcmV0admin:secret 的 Base64 编码。它不是加密,拿到请求的人可以很容易还原用户名和密码。

因此 Basic Auth 一定要配 HTTPS。HTTP 明文传输时,这层认证几乎等于把密码暴露在链路上。

Nginx 读到 Authorization 后,会从 .htpasswd 里找对应用户名的密码哈希并校验。通过就继续执行 try_filesproxy_pass;失败仍然返回 401,应用服务本身根本收不到这次请求。

两层认证分别保护什么

Nginx 的 Basic Auth 更像入口门禁。它决定的是“请求能不能先碰到这个系统”。

应用自己的登录、SSO、验证码和 RBAC 则决定“这个用户是谁,以及他进来后能做什么”。

Basic Auth 不适合承担用户权限、操作审计、单点登录和 MFA 这些职责。很多时候入口账号还会被几个内部人员共用,单靠它甚至无法区分具体操作人。所以应用原有的认证不能因为加了这一层就下掉。

它的价值是把没有入口凭据的请求挡在应用外面,减少页面信息和攻击面的暴露。

重新生成 .htpasswd

Debian/Ubuntu 上需要先装 htpasswd 命令:

bash 复制代码
sudo apt update
sudo apt install apache2-utils

第一次创建文件:

bash 复制代码
sudo htpasswd -c /etc/nginx/.htpasswd admin

命令会要求输入两次密码。这里的 -c 是 create,会创建新文件;如果文件已经存在,它会直接覆盖旧内容。这个参数最容易踩坑,新增用户时千万别带。

给已有文件新增用户,或者重置某个用户密码:

bash 复制代码
sudo htpasswd /etc/nginx/.htpasswd zhangsan

现代环境里可以明确使用 bcrypt:

bash 复制代码
sudo htpasswd -B /etc/nginx/.htpasswd zhangsan

文件内容大概是这样:

text 复制代码
zhangsan:$2y$05$...

冒号前是用户名,后面是密码哈希,不是明文密码。密码哈希仍然需要保护:它泄露后虽然不能直接登录,但弱密码仍然有被离线爆破的风险。

可以顺手限制文件权限:

bash 复制代码
sudo chown root:root /etc/nginx/.htpasswd
sudo chmod 640 /etc/nginx/.htpasswd

权限不要图省事开成 777。如果 Nginx 读不到文件,应该先看错误日志,并确认 worker 运行用户是 www-datanginx 还是其他用户。

生效前先检查

配置改完先检查语法:

bash 复制代码
sudo nginx -t

确认没问题后再重载:

bash 复制代码
sudo systemctl reload nginx

不带凭据访问时可以验证是否返回 401:

bash 复制代码
curl -I http://127.0.0.1:82/

带凭据验证:

bash 复制代码
curl -I -u zhangsan:你的密码 http://127.0.0.1:82/

如果要让监控系统访问健康检查接口,也可以只对那一个路径关闭认证:

nginx 复制代码
location = /health {
    auth_basic off;
    return 200 "ok\n";
}

这类例外要尽量少。尤其是 SPA,不要只保护首页却把静态资源或接口单独放开,不然前面加的这层门禁就只剩一个登录弹窗了。

这层门不能让服务完全隐身

加了 Basic Auth 以后,扫描器看不到页面内容,但仍然能发现域名、端口,以及服务返回了 401 Unauthorized。认证域里的文字也会出现在响应头里,所以别在 realm 里写系统名称、项目代号之类的信息。

对敏感程度更高的系统,Basic Auth 应该和 VPN、零信任网关、IP 白名单、防火墙一起用。它适合做外层防御,不是把公网服务变成内网服务的魔法。

这次加它主要不是为了替代原来的登录,而是觉得内部系统的 Logo、标题和登录页本来就不该直接送给扫描器。Nginx 在应用前面先拦一道,成本很低,也比较符合这个场景。

加载评论中...