【系列序言】公司的网络是用域名白名单和重重权限堆起来的。架构师们拍着胸脯保证,哪怕是一只苍蝇,也得对齐了OKR才能飞得进来。
确实,苍蝇飞不进来,但高墙的夹缝里总有耗子。
“墙中之鼠”只对系统夹层、权限盲区和被遗忘的暗道感兴趣。它们擅长把两个风马牛不相及的功能拼到一起,弄出些让安全合规部血压飙升的“赛博违建”。
【免责声明】本系列故事内容皆为虚构,如有雷同,纯属巧合。
小K新近入职了一家公司A。
A公司是一家业内知名的偏硬件的公司,内部有严格的网络隔离机制,小K作为研发人员,所在的网络环境采用了白名单机制,只有特定域名的网站可以访问,Google搜索、GitHub也在其范围内,内部也有专门的页面代理服务器配置了部分常用的框架文档页。
一般来说这也够用了,但开发过程中总是会有一些零碎的小问题,这些问题的答案可能就藏在某篇Google索引到的个人博客里,面对这些近在咫尺又无法触摸到的答案,小K常常叹气。
当然更重要的原因是小K平日爱好摸鱼,上班时浏览不到平日里爱看的技术论坛会觉得自己与时代脱节。这种郁闷的工作持续了一个多月。
发现裂缝
变化出现在某一天,小K在用内部的翻译平台时发现这个平台还有网页翻译功能。这时候一个疑问在他脑海里浮现出来:既然网络访问权限是根据当前网络环境和个人账号共同决定的,那这个翻译平台的服务有没有可能没被囊括其中呢?
小K把一个平日里打不开的网站粘贴进了翻译平台的输入框,敲下回车后,发现翻译平台成功带回了对应网页的内容!
像是发现了什么不得了的秘密的小K很快又尝试了更多网站,但结果并不是很乐观,翻译平台只能带回那些SSR/SSG类型的网站,对使用流行的现代前端框架构建的SPA/CSR网站就只能返回一个白屏了。
小K虽然对于这个结果不是很满意,但有了突破口也算不错的进展了,至少能靠这个看更多名单外的文档网站。
拾起锥子
若干天后,小K又在摸鱼了😡。他刷到一个叫LeetCode-Stats-Card的项目。他发现一个问题:官方文档中提到,要在GitHub Profile中展示这张信息卡,只需要加一行就好了,问题是这个域名肯定不在公司的白名单里,为什么还能看到生成的卡片呢?
小K随后在元素查看器里找到了答案,这是因为GitHub会把文档里的图片链接缓存到自家的CDN服务中,生成一条如下的固定链接。
https://camo.githubusercontent.com/xxx/yyy开凿⛏️
尤里卡!小K忽然想到,既然GitHub能把外部图片代理进来,那自己是不是也能在外面放一个浏览器,把网页截图后当成图片传回来?翻译平台正好可以用来向这个服务提交要访问的网址。经过一晚上的摸索,他做出了下面这套服务。

扩张
第二天,小K在公司里试了试,果然能用,但仍留有两个小问题。
- 能拿到的只有图片,但如果再想访问网站里的超链接或者只是想点击某个按钮展示隐藏内容就没辙了。
- 因为整个流程是单向的,客户端无法得知Camo什么时候能拿到服务器返回的最新图片,这时候访问往往只能拿到缓存的旧图,用户只能反复手动刷新,等待缓存更新。
后来小K又改进了客户端,把截图渲染到Canvas上,将鼠标点击的位置换算成实际网页的坐标,让服务器代为点击,再生成一张截图传回来,就这样实现了一定的互动式浏览效果。又发现可以通过调整截图服务返回图片时的缓存过期响应头来迫使Camo更加频繁更新缓存,这下两个小问题都解决了。
尾声
很久以后,小K有了自己的代理权限,不再需要这个工具了。这时他已经了解到一种叫服务端请求伪造(Server-Side Request Forgery,SSRF)的网络攻击手段,发现自己之前借翻译平台发请求的做法,竟然也有些相似。
