BUG-Fly BUG-Fly
  • 首页
  • BUG-EXP
  • 编程开发
  • 电脑评测
  • 生活分享
  • 友情链接
  • Fly全站协议声明
首页 › Go › 如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求

BUG-Fly
3 年前

好久都没有水文章了,记得上一次水的文章还是在上一次(废话文学),当然记得:《JS逆向|某教学CMS(x可思)请求体加密分析 》, BUG-Fly (3xittec.cn)还是在去年12月,看来是自己懒了!

0x00 缘起

如题所见,今天水的这篇文章就是解决在 Chrome Headless 模式下如何 优雅 地去触发网站的 favicon 请求。看到这里可能会产生疑问:Chrome 在 Headless 模式下不会触发网站的 favicon 请求吗?其实当我第一次听到这个问题,也和正在阅读文章的你是同样的问号脸!答案:不会触发。

这个问题的发现,还是组里的一位做资产识别的前辈发现的,当时我一脸疑惑,然后亲自做了实验发现确实如此,Chrome 在 Headless 模式下确实不会触发网站的 favicon.ico 请求。

0x01 相识

为了探究是实现和 Chrome 通信的框架问题,还是 Chrome 在 Headless 模式下的默认渲染行为。我对市面上的网络安全爬虫工具分别在 Chrome Headful 模式和 Chrome Headless 模式下做了实验。

Chrome Headless 模式

Rad

Rad 是长亭科技的著名的扫描器 Xray 集成的网络安全爬虫工具,其采用 GO 开发,采用的通信框架是rod , 是一个直接基于 DevTools Protocol 高级驱动程序。 使用 Rad 对 github 进行了测试,测试截图如下:

image-20220821175740142
image-20220821175330607

如图所示,在输出的结果中搜索未见到“favicon”字样。

HAWK-X

HAWK-X 是绿盟科技下一代智能爬虫引擎。其采用 GO 开发,是一款基于事件驱动的 Web 2.0 启发式浏览器爬虫,拥有业界领先的登录扫描方式、分布式能力等诸多特性。已经服务于绿盟科技的多款工具和产品,如 WVSS/RSAS/EZ 等。与 Rad 一样使用的通信框架也是 rod ,使用 HAWK-X 对 github 进行了测试,测试结果如下图:

image-20220821180505650
image-20220821181334458

如图所示,在输出的结果中搜索未见到“favicon”字样。

Playwright

Playwright(通信框架)是微软在 2020 年初开源的新一代自动化测试工具,它的功能类似于 Selenium、Puppeteer 等,都可以驱动浏览器进行各种自动化操作。使用 Playwright 对 github 进行测试,测试结果如下图:

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求-BUG-Fly

如图所示,在输出的结果中搜索未见到“favicon”字样。

Chrome Headful 模式

Rad

禁用 Rad 的 headless 模式,再次对 github 进行测试,测试结果如下图:

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求-BUG-Fly

从输出结果中,发现了 “favicon”字样。

HAWK-X

禁用 HAWK-X 的 headless 模式,再次对 github 进行测试,测试结果如下图:

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求-BUG-Fly

从输出结果中,发现了 “favicon”字样。

Playwright

禁用 Play的 headless 模式,再次对 github 进行测试,测试结果如下图:

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求-BUG-Fly

哦,有着不一样的发现:PlayWright 的 headful 模式下竟然也没有 favicon 相关的请求。按道理说在 headful 模式下,应该会有 favicon 相关请求的,在 F12 Network选项卡中也能查看得到,此时敏锐的嗅觉告诉我,此事必有蹊跷!

经过上述实验,可以得到以下实验结果表格:

工具\框架HeadlessHeadful
Rad(rod)未触发触发
HAWK-X(rod)未触发触发
PlayWright未触发未触发

看来 PlayWright 的框架本身另有玄机!

0x02 相知

从上述的实验结果,是否可以就此下定结论:无法触发 favicon 的请求,是因为 Chrome 在 Headeless 模式下的渲染行为所为,而和框架本身无关?

Playwright 反常结果的求证

PlayWright 本身也是基于 CDP 协议实现的,结果应与另外两者相同才是,这不免产生怀疑:Playwright 框架本身对 favicon 相关请求做了过滤。

开始着手在 PlayWright 的 issue 里寻找线索,搜索 favicon 关键字,果然有东西:

[BUG] page.on('request') is not capturing favicon.ico URI #7493 这条 issue 吸引了我的注意,点进去在对话中发现:

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求-BUG-Fly

简单且核心地翻译一下:我们目前有意的过滤掉 favicon 的请求。

本节开始的怀疑有了答案:PlayWright 实在框架层面对 favicon 相关请求做了过滤。

不过为什么会对 favicon 请求做过滤呢?继续往下探索,发现了下面这条 pr:

feat(network): ignore favicon requests - these are too unpredictable #533

如何优雅地在 Chrome Headless 模式下触发网站的 favicon 请求-BUG-Fly

理由:favicon 相关的请求是不可预测的。

确实如此,有些站点的 favicon 并非是 "host:port/favicon.ico",亦或是存放在其他路径下或者是其他image类型的,例如我的站就是 .png 结尾的。这确实会给程序带来不稳定因素。

现在一切都已经真相大白,此时我们可以下定结论就是:Chrome 在 Headeless 模式下不会去触发 favicon 的请求。这是 Chrome Headless 的默认渲染行为。

0x03 相爱

经过前文的分析:Chrome 在 Headeless 模式下不会去触发 favicon 的请求。这是 Chrome Headless 的默认渲染行为。 已经明白了原因,那就去解决它!

Chrome Flags

起初打算尝试通过一些 Chrome 的启动参数,看看能不能有收获,但是当我看到全部参数的时候我陷入了沉思:

image-20220821212247747

我看了一下这些参数至少有百余个之多,这一个个试得多麻烦,这条 Pass。

工匠精神——造轮子

打算在 Headless 模式下直接使用,Python/GO 的HTTP请求库,拼接相应 favicon 的连接,直接发起请求。但是我觉得这样不够优雅,既然都已经选择了 Chrome Headless 为何还要在 Chrome Headless 之外额外的发起请求呢!实在是不够优雅。既然前边这几个通信框架都是基于 CDP 协议实现的那不为何自己造轮子,实现一个自己的 TriggerFavicon() 的 API呢。

思路很直接也很粗暴:既然 Chrome 在 Headless 模式下选择不去渲染触发 favicon 请求,那我们就“强迫”它把它应该做的事给做了。

Rod 版

// golang
​
func (b *Browser) IsHeadless() bool {
res, err := proto.BrowserGetBrowserCommandLine{}.Call(b)
utils.E(err)
for _, v := range res.Arguments {
if strings.Contains(v, "headless") {
return true
}
}
return false
}
​
​
​
func (p *Page) TriggerFavicon() error {
if !p.browser.IsHeadless() {
return errors.New("Browser is headful")
}
proto.PageSetLifecycleEventsEnabled{Enabled: true}.Call(p)
wait := p.EachEvent(func(e *proto.PageLifecycleEvent) bool {
return e.Name == "DOMContentLoaded"
})
wait()
js := `() => {
   faviconElement = document.querySelector("link[rel~=icon]");
   href = (faviconElement && faviconElement.href) || "/favicon.ico";
   faviconUrl = new URL(href,window.location).toString();
   xhr = new XMLHttpRequest();
   xhr.open("GET",faviconUrl);
   xhr.send();
   xhr.addEventListener("readystatechange",function () {
       if (xhr.readyState === 4) {
           if (xhr.status>=200 && xhr.status <= 300) {
               return faviconUrl;
           }
       }
   });
}`
_,err:= p.Evaluate(Eval(js).ByUser())
if err != nil {
return err
}
p.unsetJSCtxID()
return nil
}

代码逻辑很简单:

实现了两个方法:

  • IsHeadless:用于判断吃否处于 Headless 模式下。
  • TriggerFavicon:判断处于 Headless 模式下,在 Page DOM 渲染完成后注入触发favicon 请求的 JS

PlayWright 版

# Python
​
async def trigger_favicon(page: Page):
   script = """() => {
  faviconElement = document.querySelector("link[rel~=icon]");
  href = (faviconElement && faviconElement.href) || "/favicon.ico";
  faviconUrl = new URL(href,window.location).toString();
  xhr = new XMLHttpRequest();
  xhr.open("GET",faviconUrl);
  xhr.send();
  xhr.addEventListener("readystatechange",function () {
      if (xhr.readyState === 4) {
          if (xhr.status>=200 && xhr.status <= 300) {
              return faviconUrl;
          }
      }
  });
}"""
   page.on("domcontentloaded", await page.evaluate(script))
   return True

代码逻辑和 Rod 版的大差不差,唯独少了一个 IsHeadless的方法,这是因为 PlayWright 是否是 Headless 模式下都不会拿到 favicon 的请求,所以也就没有必要去实现了,正因如此,这个 API 在 PlayWright 的使用前提是在 CDPSession模式下:

 page = await browser.new_page()
client = await page.context.new_cdp_session(page)
await client.send("Fetch.enable")
await page.route("**/*", lambda route: resp_route(route, output_list, True))
​
await page.goto("https://github.com")
await trigger_favicon(page)

0x04 总结

上述两个版本都经过实测,稳定触发。当然我觉得这还不是最优雅的,最优雅的还是通过 Chrome 自身去解决这个事情,例如,启动参数(Flags)或者其他方法。如果有对 Chrome 有深入研究的朋友,知道其它好的解决办法,欢迎在评论区留言,探讨交流。

PlaywrightPython
2
0
BUG-Fly
写BUG飞起的Coder
JS逆向|某教学CMS(x可思)请求体加密分析
上一篇
你的 Go 程序会吃文件吗
下一篇

评论 (0)

请登录以参与评论
现在登录
    发表评论

猜你喜欢

  • 忆过往,启新程
  • 你的 Go 程序会吃文件吗
  • JS逆向|某教学CMS(x可思)请求体加密分析
  • Python 函数

词云

2020 (1) Flask (1) Go (1) JS逆向 (1) Linux (1) Playwright (1) PySide2开发 (1) Python (13) Python实战项目 (5) 固原一中 (1) 国庆70周年 (1) 开源 (1) 数据结构和算法 (2) 数组 (1) 新年贺词 (1) 新月诗刊社 (3) 电脑评测 (3) 软件教程 (3) 雨雯公益 (1) 音乐 (3)

BUG-Fly

写BUG飞起的Coder
34
文章
5
评论
324
获赞
  • 首页
  • 友情链接
Copyright © 2019-08-20-2025 BUG-Fly. Designed by BUG-Fly.

Fly小站已经运行:

津ICP备19007312号
技术基佬基地: KRUNK ZHOU Legna 科技
  • Python13
  • Python实战项目5
  • 新月诗刊社3
  • 音乐3
  • 电脑评测3
  • 首页
  • BUG-EXP
  • 编程开发
  • 电脑评测
  • 生活分享
  • 友情链接
  • Fly全站协议声明