Captive Portal

概述

在校园、机场、酒店、银行、咖啡店、肯德基、麦当劳等场所连接 WiFi 时,你发现不用输入WiFi 的密码,而是弹出一个网页。有的需要你输入手机号并点击获取验证码,并将通过短信获取的验证码输入,你就可以正常上网了。有的需要你输入正确的账号和密码并点击登录,然后你也可以上网。

那么问题来了,为什么我在家里连接 WiFi 时,并没有弹出所谓的网页呢?经过我在浏览器的疯狂搜索和浏览,原来使用一种叫做 “Captive Portal” 的技术实现。

captive portal

captive portal(中文称之为 强制门户)是一种通过 Web 浏览器访问的网页,在授予新连接的 Wi-Fi 或有线网络用户更广泛的网络资源访问权限之前,会向他们显示该网页。强制门户通常用于显示着陆页或登录页面,该页面可能要求身份验证、付款、接受最终用户许可协议可接受使用政策、完成调查或提供主机和用户都同意遵守的其他有效凭证。强制门户广泛用于各种移动和步行宽带服务,包括有线和商业提供的 Wi-Fi 和家庭热点。强制门户还可用于提供对企业或住宅有线网络的访问,例如公寓、酒店房间和商务中心。

image-20241116191019492

实现原理

自动弹出Captive Portal认证页面实现原理:终端(手机、平板、电脑等设备)连接到WiFi后(一般该WiFi加密方式为OPEN),主动发出HTTP的探测请求报文,检测目的地址是否可达,以及回应的内容是否符合预期,以此来判断接入的网络是否需要进行Captive Portal认证。终端探测的目标URL一般是固定的网址,各终端或APP应用存在差异。如果目标URL不可达或回应内容不符合预期,那么终端会调用浏览器再次发出HTTP请求,路由器拦截到此请求进行重定向,实现自动弹出Captive Portal认证页面的功能。

某些终端无法自动弹出页面的原因:

  • 终端不会主动发出探测请求报文。
  • 终端可以发出一次探测请求报文,但是由于某些安装的APP影响导致终端无法调用浏览器再次发出请求,无法自动弹出页面。
  • 大部分安卓手机,自动弹portal功能,依赖用户手动点击ssid界面进行触发。

不同平台对应的测试固定网址如下:

Platform Test URL Expected response
Apple (MacOS/IOS Family) http://captive.apple.com/hotspot-detect.html “Success” (plain text)
Google (Android/ChromeOS) http://connectivitycheck.gstatic.com/generate_204 HTTP status code 204 with an empty body
Windows http://www.msftconnecttest.com/connecttest.txt “Microsoft Connect Test” (plain text)
NetworkManager (GNOME) http://nmcheck.gnome.org/check_network_status.txt “NetworkManager is online” (plain text)
NetworkManager (KDE Plasma) http://networkcheck.kde.org/ “OK” (plain text)

实现方式

captive portal的实现方式:

  • DNS拦截:将所有 DNS 请求指向自己的 portal 地址
  • HTTP重定向:直接劫持 HTTP/HTTPS 流量,响应自己的页面

注意:仅网关实现了Captive Portal还不够,还不能自动弹出认证页面。需要操作系统支持才能实现在网络连接后主动弹出认证页面的功能。

抓包

今天(2024-11-17)上午我连接上了图书馆的WiFi(ssid=reader),在Ubuntu系统消息中心上弹出了Web认证消息,点击后就可以跳转到网页。此时我的WifI图标还是带问号,但我输入正确的账号和密码,就成功跳转到图书馆的官网主页,同时WiFI图标的问号消失。

我使用 wireshark 把这个过程的数据包都抓下来了。当我点击 reader WiFi网络时,此时我其实已经连接上了 reader WiFi网络,我的IP地址是 10.5.243.105,只是无法接入互联网。在wireshark 中出现了很多 mDNS 数据包,按照正常的步骤,Ubuntu 应该会尝试去访问固定的URL,那么首先要进行DNS解析,所有先把 DNS 数据包过滤出来。

Ubuntu 会发出 DNS 数据包,这个数据包的作用是:请求本地网关帮我解析域名 connectivity-check.ubuntu.com ,如果本地网关(网络IP是 10.5.9.9)也不知道,接着报数据包转发给上一级 DNS 服务器。可是从抓包结果来看,本地网络显然是知道该域名对应的IP地址的。

image-20241117100111698

Ubuntu 拿到了域名 connectivity-check.ubuntu.com 的IP地址,接着就会发出 HTTP 请求,URL是 http:connectivity-check.ubuntu.com:80 ,所有我在 wireshark 中把 HTTP 数据包过滤出来。

image-20241117102414335

HTTP 请求成功了,看一下 HTTP 应答的内容是什么?这时候有意思的地方来了,HTTP 应答报文为如下部分:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Frame 662: 562 bytes on wire (4496 bits), 562 bytes captured (4496 bits) on interface wlp1s0, id 0
Ethernet II, Src: HuaweiTe_28:33:a0 (e8:ea:4d:28:33:a0), Dst: 60:e9:aa:d3:81:93 (60:e9:aa:d3:81:93)
Internet Protocol Version 4, Src: 91.189.91.97, Dst: 10.5.243.105
Transmission Control Protocol, Src Port: 80, Dst Port: 42282, Seq: 1, Ack: 88, Len: 508
Hypertext Transfer Protocol
HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 443\r\n
\r\n
[HTTP response 1/1]
[Time since request: 0.003968927 seconds]
[Request in frame: 661]
[Request URI: http://connectivity-check.ubuntu.com/]
File Data: 443 bytes
Line-based text data: text/html (9 lines)
<HTML>\r\n
<HEAD>\r\n
<TITLE> Web Authentication Redirect</TITLE>\r\n
<META http-equiv="Cache-control" content="no-cache">\r\n
<META http-equiv="Pragma" content="no-cache">\r\n
<META http-equiv="Expires" content="-1">\r\n
<META http-equiv="refresh" content="1; URL=http://10.5.12.50:8445/portal?wac%2Dmac=f4fbb823682f&redirect%2Durl=http%3A%2F%2Fconnectivity%2Dcheck%2Eubuntu%2Ecom&ssid=reader&uaddress=10%2E5%2E243%2E105&umac=60e9aad38193">\r\n
</HEAD>\r\n
</HTML>\r\n

而上述内容中的URL就是Web认证网页,用户只要在Ubuntu的消息中心点击WiFi认证消息,就会向这个URL发送HTTP请求,然后出现WiFI认证页面。

疑问

1、如果我不想在Ubuntu开启网络检测,该如何设置?

其实在Ubuntu 22.04 中,网络检测是默认开启的,当我们连接到一个有限网络或无线网络时,Ubuntu 会尝试去访问固定的URL来判断是否可以访问互联网。

image-20241117110835593

参考

如何搭建类似麦当劳店中需登录认证的wifi

使用ESP32C3开发板实现一个强制门户(Captive Portal)

树莓派搭建wifi热点

树莓派搭建captive-portal

Captive_Portal_ESP