📘 第八章 确认访问用户身份的认证
📄 一句话概括

BASIC 认证就是「账号:密码」做一次 base64 放进 Authorization 首部。 base64 不是加密,所以它必须配 HTTPS;而且它没有注销机制。

📝 本节和前面几节的关系

上一节讲了认证的概念,这一节是历史上最简单的一种实现——简单到几乎不能单独使用。

一、两步就完成

第一步裸着请求,服务端回 401 加一行 WWW-Authenticate: Basic realm="secret-zone"。 客户端看到它才知道:用 Basic 方案,去认证这个区。 第二步把「账号:密码」用 base64 编一下放进 Authorization,重发。 Demo 里明文 14 字节,base64 后 20 字节,膨胀 43%。 膨胀率不是固定的 33%——那是三个字节一组算出来的理论值,字节数除不尽 3 时会多出一组补 =, 所以短字符串的涨幅更大。

二、base64 不是加密

Demo 里直接把那串 Ym96YWk6cGE1NXdvcmQ= 解回来,得到 bozai:pa55word——不需要密钥,不需要算力。 所以 BASIC 的安全性完全依赖传输层:跑在 HTTPS 上,中间人看不到; 跑在 HTTP 上,等于把密码贴在请求里。 这一点经常被误解成「加了个编码就安全了」。判断方法很简单:能不能用一行代码还原?能,那就不是加密。

三、三个绕不过去的毛病

一、密码每请求都过网。不是登录时发一次,是每一个请求都带。一旦有一条请求走漏,凭据就长期泄露。 二、没有注销机制。凭据存在浏览器里,用户点「退出」也只是页面上的事,下次请求它照样自动带上。 真正的注销只能关浏览器。 三、凭据是长期的。密码改了之前,那串 base64 一直有效——它不像会话 ID 那样能靠超时失效。 所以它今天的定位很清楚:内网工具、路由器管理页、API 测试脚本。面向用户的系统早就不用了。

四、动手 Demo:401 挑战,base64 应答

一个只认 Basic 的服务端。 先裸着请求看它给的挑战,再手工算出 base64 凭据重发,最后量一下编码前后的字节数。

raw_cred = (USER + u':' + PW).encode('utf-8')      # 14 字节
b64 = base64.b64encode(raw_cred).decode('ascii')    # 20 字节

X.req(u'GET', u'/secret', u'127.0.0.1',
      extra=[(u'Authorization', u'Basic ' + b64)])

实跑输出:

--------------------------------------------------------------------
BASIC:把密码 base64 一下发过去
--------------------------------------------------------------------
  第一步,裸着问:
    HTTP/1.1 401 Unauthorized
    Basic realm="secret-zone"
    客户端看到这一行,才知道要用 Basic、去认证 `secret-zone` 这个区。

  第二步,把「账号:密码」base64 一下:
    原文    bozai:pa55word  (14 字节)
    base64  Ym96YWk6cGE1NXdvcmQ=  (20 字节)
    膨胀    43%

  第三步,带上它再问:
    HTTP/1.1 200 OK
    正文 36 字节:登录之后才看得到的页面。
    服务端被访问 1 次(两次请求)

  base64 不是加密——它只是个编码,谁都能解回来:
    解回来就是 bozai:pa55word

  所以 BASIC 有三个绕不过去的毛病:
    一、密码每请求都明文过网(base64 后的明文),必须配 HTTPS 才有意义;
    二、没有注销机制——浏览器关掉才会忘掉凭据,用户没法「退出登录」;
    三、凭据在浏览器里存整个会话,一旦机器被人碰到就是长期泄露。
  它今天还在用的场合基本只剩:内网工具、路由器管理页、以及各种 API 的测试脚本。

裸请求回 401,首部是 WWW-Authenticate: Basic realm="secret-zone"。 没有这一行,客户端拼不出凭据——这正是 6.5.9 讲过的机制。 明文 14 字节、base64 20 字节,膨胀 43%。数字本身不重要,重要的是它证明这只是编码,不是加密。 带上凭据后回 200,正文 36 字节。两次请求之间,密码在网线上走了两遍。 解回来就是 bozai:pa55word——所以 BASIC 必须配 HTTPS,没有例外。

30 秒实验

  1. 把密码改成一个中文字符串,看 base64 后的长度怎么变。
  2. 故意把 base64 写错一个字符,看服务端回 400 还是 401。
  3. 想一想:为什么 BASIC 认证没法做「退出登录」?

常见误区

  1. 「base64 是一种加密」——它是编码,谁都能解。
  2. 「配了 HTTPS 就万无一失」——传输安全了,但注销和长期凭据的问题还在。
  3. 「base64 一定膨胀 33%」——短字符串因为补位,膨胀率更高。
自测三问(点开对答案) 1. BASIC 认证的凭据是什么形式? —— 「账号:密码」做 base64,放在 `Authorization: Basic ...` 里。 2. 为什么它必须配 HTTPS? —— base64 可逆,等于明文传密码。 3. 它最大的使用缺陷是什么? —— 没有注销机制,凭据长期有效且每个请求都带。

BASIC 把密码送上去了。DIGEST 想了个办法:只发摘要,不发密码。

相关笔记