Charles HTTPS 抓包、Rewrite 与移动端调试手册
Structure 里没有请求时,不要先碰证书
从 Charles 官方安装页进入下载或 APT/YUM 仓库。Linux 桌面也可解压官方 tar.gz,在解压目录以普通用户运行 bin/charles;不要用 sudo 启动 GUI。Charles 5 为商业产品,先确认试用或组织授权。进入 Proxy > Proxy Settings 核对 HTTP Proxy 端口 8888,在 Proxy > Recording Settings 或工具栏确认 Recording 已启用;停止记录、过滤隐藏和请求未经过代理都可能使列表空白。
Charles 是桌面前向代理。客户端先把 HTTP 或 HTTPS 连接交给 Charles,Charles 再连接目标服务。确认 Recording 已启用、清除显示过滤后仍看不到请求,再检查系统代理是否接管、目标应用是否有独立代理设置、手机与电脑是否网络可达,以及 VPN 路由和代理绕过规则。反复安装 Charles Root Certificate 不会修复代理路径,因为 TLS 解密发生在代理连接建立之后。
先用受控测试地址跑一条普通 HTTP 请求。Charles 5 安装后会在桌面会话中监听本地代理端口;确认端口没有被其他程序占用,再让浏览器或命令行显式指向它。下面的命令不依赖系统代理,适合判断进程、地址和端口是否成立:
curl -q --noproxy '' --fail-with-body --proxy http://127.0.0.1:8888 \
--connect-timeout 3 --max-time 8 http://example.com/ \
-o /dev/null -w '%{http_code}\n'状态码出现在终端,并且 Charles 的 Sequence 或 Structure 视图出现同一请求,才能继续处理 HTTPS。若 curl 报连接拒绝,检查 Charles 是否正在运行、监听端口是否改过以及本机安全软件是否阻断;若 Charles 收到连接却没有上游响应,再查代理主机自身的 DNS、企业出口代理和目标服务。客户端到 Charles、Charles 到上游是两段不同连接,错误不能混成一句“抓包失败”。
Structure 按 host 组织会话,适合从域名进入;Sequence 保留时间顺序,适合还原登录跳转、重试和并发请求。排查一次移动端登录时,可以先在 Structure 里确认目标 host 是否出现,再回 Sequence 查看它前后的认证跳转。不要一开始就用大范围过滤隐藏所有非目标会话;先证明流量存在,再用 host、path、method 和状态码收窄。
SSL Proxying 只打开获准的 host
HTTPS 客户端通常先发出 CONNECT api.example.test:443。Charles 与真实服务完成一条 TLS 连接,同时为该 host 动态生成一张由 Charles Root Certificate 签名的证书,交给客户端建立第二条 TLS 连接。客户端只有信任这张根证书,Charles 才能看到 HTTP 明文。
先在 Proxy > SSL Proxying Settings 把获准的目标 host 加入 SSL Proxying 列表;运行下方示例时填写 example.com、端口 443,暂时不要向系统或浏览器安装 Charles 根证书。Charles 的 SSL Proxying 说明要求按 host 明确启用;通配符 * 会解密所有经过代理的 HTTPS 流量,不适合作为日常默认值。已有连接可能继续复用旧会话,规则变更后应关闭目标应用连接再复现,必要时重启 Charles。
命令行实验不必把 CA 永久放进系统信任库。在 Linux 桌面终端创建本次独占目录,记下打印的绝对路径,然后在 Help > SSL Proxying > Save Charles Root Certificate 选择 PEM 格式,把公开根证书保存为该目录下的 charles-root-public.pem。只导出公开根,不导出含私钥的 PKCS12:
CHARLES_LAB="$(mktemp -d)"
printf '保存证书到 %s/charles-root-public.pem\n' "$CHARLES_LAB"保存后先核对文件存在,再让本次 curl 显式信任它。下例使用 https://example.com/ 检查代理解密;需要服务端日志对照时,换成已获准且证书链有效的测试服务,并把 SSL Proxying host 列表和 URL 一起换掉:
test -s "$CHARLES_LAB/charles-root-public.pem" &&
curl -q --noproxy '' --fail-with-body --proxy http://127.0.0.1:8888 \
--cacert "$CHARLES_LAB/charles-root-public.pem" \
--connect-timeout 3 --max-time 8 \
https://example.com/ \
-o /dev/null -w '%{http_code}\n'预期终端返回 200,Charles 中出现同一 host 的明文请求;如使用自己的测试服务,可给请求另加 -H 'X-Debug-Case: charles-baseline',再与服务端日志对照。若这台机器的 curl 此前没有信任 Charles 根,可在相同代理、URL 和 host 规则下只去掉 --cacert;排除环境 CA 覆盖后,结果应为证书不受信、退出码 60:
curl_rc=0
env -u CURL_CA_BUNDLE -u SSL_CERT_FILE -u SSL_CERT_DIR \
curl -q --noproxy '' --proxy http://127.0.0.1:8888 \
--connect-timeout 3 --max-time 8 \
--silent --show-error --output /dev/null https://example.com/ || curl_rc=$?
printf 'curl_rc=%s\n' "$curl_rc"
test "$curl_rc" -eq 60恢复 --cacert 后应再次成功。已经安装过该根的机器不能用这个负例,需要独立的未信任客户端,不能为制造失败直接删团队正在使用的根。若返回连接拒绝、超时或其他状态,也不能拿它证明 CA 不受信。如果连 CONNECT 都不存在,仍应回到代理接入层;如果只有 CONNECT 而没有明文,才检查 CA、SSL Proxying host 和客户端 pinning。
浏览器或手机也要参与实验时,才按 Charles 官方设备证书说明为实际使用的客户端安装根并记录安装位置。Windows/macOS 的 Help > SSL Proxying > Install Charles Root Certificate、Firefox 独立证书库、iOS 的描述文件和 Android 调试应用各走不同路径;Linux 的浏览器或 curl 也不能仅凭 GUI 菜单声称已信任。手机接入时,Charles 需要监听电脑的局域网地址,手机 Wi-Fi 代理填写电脑 IP 与 Charles 端口。监听地址对局域网开放后,主机防火墙必须只允许测试设备来源,公共网络不得暴露代理端口。iOS 安装描述文件后还需要在证书信任设置中显式启用完整信任;Android 应用是否接受用户安装 CA 由应用网络安全配置决定。拒绝代理 CA 可能是应用安全策略正常工作,不应为了抓包去修改未获授权的生产包。
Map Local 是替换响应,不是修改服务器
Map Local 把匹配的远程 URL 映射到本地文件。它适合验证前端对某个 JSON、脚本或静态资源变化的处理,不会把文件写回服务器。先创建 fixtures/profile-expired.json,内容为 {"state":"expired"}。进入 Tools > Map Local,启用功能并 Add 一条映射:Map From 选择测试协议、Host、Port 和准确 Path,Map To 选择该文件。官方 Map Local按协议、主机、端口和路径匹配,查询串不参与本地文件查找;不能依赖 query 区分两份本地文件。找不到对应本地文件时会继续请求上游,应核对实际命中和服务端日志。
验证时给请求加入独立调试标识,同时观察三个结果:Charles 命中 Map Local,客户端收到本地文件内容,服务端没有对应响应体读取。关闭 Map Local 后重新请求,客户端恢复真实上游响应。只有这组正反证据齐全,才能把客户端分支变化归因于本地映射,而不是缓存、灰度或服务端恰好返回了相似内容。
Map Remote 把一个远程地址改送到另一个远程地址,适合在兼容环境间切换。它会改变真实上游,风险高于 Map Local。目标必须是同一授权范围内的测试服务,认证 Cookie、Host、CORS、证书与数据隔离需要重新判断。把生产域名映射到测试环境,或把测试凭证送到另一套环境,都可能造成跨环境数据泄露。
Map Local 和 Map Remote 都有 enable 开关。团队共享配置时应把规则名称写成场景和失效条件,而不是“临时规则”;任务结束先关闭规则,再用一次未命中的请求证明流量已经回到真实路径。
Rewrite 与 Breakpoints 会改变正在观察的事实
Rewrite 可以按 location 集合匹配 host、path、query、请求头、响应头和正文,并执行增加、替换或删除。它适合模拟特性开关、响应头、缓存策略和有限的错误体,但规则范围必须比抓包过滤更严格。过滤器只影响界面展示,Rewrite 会改变线上传输的字节;两者不能用同一种“看起来只剩目标请求”的直觉管理。
一个稳妥的 Rewrite 实验只对测试 host、固定 path 和 X-Debug-Case 命中。先记录不启用 Rewrite 的基线响应,再打开规则给响应增加 X-Debug-Proxy: charles,验证客户端确实进入目标分支;随后关闭规则并证明响应头消失。若规则修改正文,还要处理 Content-Type、编码、压缩和长度,避免由格式损坏制造与业务无关的失败。
Breakpoints 会在请求发往上游前或响应返回客户端前暂停会话,允许人工编辑后继续。它适合一次性的交互探索,不适合稳定回归:人工停顿会改变超时、重试和并发顺序。断点未释放时,客户端看到的可能只是超时。复现性能、流式响应、WebSocket 或竞态问题时应关闭 Breakpoints,改用可重复规则或服务端夹具。
Charles 还提供 Throttle,用带宽、延迟和可靠性参数模拟较差网络。这里同样要先记录未限速基线,再启用命名 profile,并同时记录客户端超时、重试次数和 Charles 时序。代理增加的排队与两段连接本身也会改变网络表现,关键结论仍需用不经过 Charles 的请求和服务端 trace 对照。
.chls 会话不是普通附件
Charles session 的 .chls 文件可能保存完整 URL、Cookie、Authorization、请求体、响应体、内部域名、设备标识和文件内容。即使界面里只选中一条会话,导出范围也必须再次确认。优先复制必要的请求行、状态码、脱敏头和相对时序,不默认把整个 session 上传工单。
保存原始会话时使用受控临时目录和最短留存窗口,只允许当前处理者访问。分享前人工检查查询参数、认证头、Set-Cookie、JSON 字段、表单、上传文件和二进制响应;仅删除可见的 Authorization 不能证明压缩正文或其他会话没有秘密。会话若必须进入问题系统,应记录脱敏人、复核人、保留原因和销毁时间点。
大响应、长连接和持续抓取会推高 Charles 内存和会话文件体积。先限定 host 和时间窗,再决定是否保留 response body;看到内存持续增长时停止新采集并保存最小证据,而不是等桌面进程失去响应。一次调试的容量上限应由测试流量和终端可用空间决定,不用“抓十分钟”代替字节数与会话数约束。
退出要让网络恢复到没有 Charles 的状态
先关闭 Breakpoints、Rewrite、Map Local、Map Remote 和 Throttle,再停止 Recording。随后解除浏览器、系统或手机的代理设置;只做 curl 显式 CA 实验的读者没有安装系统根,不需要从信任库删除任何证书。若后来为浏览器或手机安装过 Charles Root Certificate,应按安装时记录的设备和 store,用指纹核对后从该处撤销信任。最后关闭 Charles 并清理 .chls、导出文件和临时 fixture。若曾开放局域网监听,还要撤销防火墙例外。
本篇 curl 实验导出的公开根可在确认没有其他实验引用后单独清理,不触碰 Charles 自身的 CA 与私钥:
test -n "$CHARLES_LAB" && test -f "$CHARLES_LAB/charles-root-public.pem" &&
rm -i -- "$CHARLES_LAB/charles-root-public.pem" &&
rmdir -- "$CHARLES_LAB"清理后的反证很直接:不走代理访问测试服务应成功;显式请求已关闭的 127.0.0.1:8888 应连接失败;曾安装根的证书库按指纹查不到该 Charles Root Certificate;进程和监听端口不存在;原先命中 Map 或 Rewrite 的调试标识不再出现。Charles 的商业授权、配置备份和升级由团队资产管理,但抓包 CA 与会话数据不应随个人配置备份长期扩散。
当工作主要依赖桌面交互、移动端引导、Map 和断点时,Charles 的路径清晰。需要跨平台团队协作与 Fiddler Rules 时应进入 Fiddler;需要代码审查、CI 夹具和非交互流量处理时更适合 mitmproxy。三者都不能替代未授权生产流量审计,也不能绕过客户端原本应当生效的证书固定策略。
