Nginx 访问认证分析

1,730 阅读3分钟

场景描述

使用 Nginx 部署一下静态网页时,出于安全性案例,需要对用户进行访问限制。而 Nginx 恰好提供了访问认证的功能Restricting Access with HTTP Basic Authentication, 主要是配置简单,只需要如下简单的几步就可以实现一个认证:

// 1. 在linux上面使用httpd-tools生成密钥
htpasswd -c /etc/nginx/htpasswd hello  // 回车 并 输入避免即可

// 2. 配置 Nginx
location /api {
    auth_basic          "Login";
    auth_basic_user_file /etc/nginx/htpasswd; 
    ...
}

配置后,就可以看见一个登录框了 使用输入正确的账号、密码,Nginx 才会响应正常的内容,点击取消就是 401 未授权页面了:

场景分析

浏览器是怎么知道登录?

可以简单对比一下 Nginx 配置了 auth_basic 前后的Nginx响应的变化。

配置 auth_basic后,初次访问 statusCode 会变成 401,且reponse header中会新增响应头:

WWW-Authenticate: Basic realm="Login"

401 比较好理解,就是没有访问权限。但 WWW-Authenticate 作用是啥?

HTTP 身份验证中有介绍到:

RFC 7235 定义了一个 HTTP 身份验证框架,服务器可以用来针对客户端的请求发送 challenge (质询信息),客户端则可以用来提供身份验证凭证。质询与应答的工作流程如下:服务器端向客户端返回 401(Unauthorized,未被授权的) 状态码,并在 WWW-Authenticate 首部提供如何进行验证的信息,其中至少包含有一种质询方式。之后有意向证明自己身份的客户端可以在新的请求中添加 Authorization 首部字段进行验证,字段值为身份验证凭证信息。通常客户端会弹出一个密码框让用户填写,然后发送包含有恰当的 Authorization 首部的请求。

WWW-Authenticate 其实就是标准的响应头,目的就是告诉浏览器需要请求需要登录校验。实际测试过程中,普通浏览器可用,微信中不可用,可以通过代码控制 Webview 是否开启Http的认证。

WWW-Authenticate的值由两部分组成:

WWW-Authenticate: <type> realm=<realm>

type一般为 Basic,还有些不常用,在Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry 规范中有维护,realm就是一个安全区域的描述,简单理解就是一个命名空间的作用,我需要用的认证场景没有体现其价值,具体可以看What is the “realm” in basic authentication

验证过程是怎样的?

在浏览器输入用户名、密码后,浏览器将用户名、密码以下述格式转化:

base64(<userName>:<password>)

然后放在请求头进行传递 再浏览器可以通过 atob解码 base64, 上面的密钥解码数据为:

Nginx难道这个头部信息后,和 /etc/nginx/htpasswd 中的保存的用户信息进行比对验证即可。

使用Node实现一个简单认证服务

使用Node实现也比较简单,核心就是 statusCode + 请求头信息设置, 简易的demo为:

const http = require('http')

http
  .createServer((req, res) => {
    if (req.headers.authorization) {
      // 可以解析里面内容,判断userId&password是否正确
      res.write('hello world')
      return res.end()
    }
    res.statusCode = 401
    res.setHeader('WWW-Authenticate', 'Basic realm="Login"')
    res.end()
  })
  .listen(8132)

效果为:

总结

通过使用 Http 提供的认证规范 WWW-Authenticate,结合 Nginx 提供的认证功能,可以很简单的实现一个简易的登录认证过程,而且浏览器认证都是会话级别的,也就是说,只要不关闭浏览器,就无需重新认证,有一定的安全性,同时体验也挺好的。