秋来冬风的博客

如何进行浏览器完整性检查

目录

我的上一篇博客,提到了浏览器完整性检查,本文对此进行探讨。

Context

在本文中,浏览器完整性检查是指识别非来自人操作浏览器来的HTTP流量。

相关理论

人操作浏览器,典型场景是发送一个GET或POST请求,然后得到一个html响应内容。

这在期间有许多的特征可以用来检测请求流量是否来自人,而非机器发出。

例如

  • 特定HTTP头(Accect等)
  • 特定能力(能执行指定js脚本)

一些检测方法

示例

这是一个示例,进行简单的浏览器完整性检查

		ua := ctx.GetHeader("User-Agent")
		u := useragent.Parse(ua) // "github.com/mileusna/useragent"
		if u.Name == "" || u.OS == "" {
			ctx.String(http.StatusForbidden, "User-Agent验证未通过")
			ctx.Abort()
			return
		}
		//TODO:进行更多验证
		if (!strings.Contains(ctx.Request.Header.Get("Accept"), "text/html") && !strings.Contains(ctx.Request.Header.Get("Accept"), "*/*")) ||
			!strings.Contains(ctx.Request.Header.Get("Accept-Encoding"), "gzip") ||
			ctx.Request.Header.Get("Accept-Language") == "" ||
			ctx.Request.Header.Get("Sec-Fetch-Site") == "" {
			ctx.String(http.StatusForbidden, "浏览器完整性验证未通过")
			ctx.Abort()
			return
		}

HTTP头

通过以下这些HTTP头可以用来进行浏览器完整性检查

  • User-Agent:这是最常用的,用来提升请求来源,浏览器通常设置为类似Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/147.0.0.0 Safari/537.36 Edg/147.0.0.0的字符串,而各个编程语言则默认设置为类似Go-http-client/1.1这种标明了自己身份的字符串,尽管可以修改伪装,但这仍然是很有用的一种检测方法。
  • Accept:通过浏览器包含text/html,表面它可以接收网页,OPTIONS请求时则包含*/*,对于伪装了User-Agent的流量,可能不会伪装其它标头
  • Accept-Encoding,Sec-Fetch-Site,Accept-Language,Sec-Ch-Ua,Sec-Ch-Ua-Mobile,Sec-Ch-Ua-Platform,Sec-Fetch-Dest,Sec-Fetch-Mode是一些相对在伪装非来自人的机器流量时,更冷门的HTTP头,但在真实浏览器中通常是存在且有相应含义内容的。
  • Referer: 浏览器里跳转而来的请求,通常有这个HTTP头,这可以用来排除一些不是来自真人操作浏览器的网络API调用。
  • 自定义头:可以通过自定义一些HTTP头,更好的帮助服务器检查流量。比如设置GET请求后js设置一个CSRF-TOKEN,POST请求时检查Cookie中是否包含此。

执行js能力

如果是真实的用户操作浏览器,一般不会禁用js(特殊情况不讨论),而使用各种编程语言实现的爬虫,默认不具备执行js的能力,往往需要外部库,或调用无头浏览器。 利用这一点,我们可以进行浏览器完整性检查。

一些思路如下

  • 响应请求前返回js脚本执行一些hash运算,比如服务器给字符串x,请求者算SHA512(x)并返回结果给服务器,检查是否能正确执行js脚本
  • 检查js的navigator.webdriver是否true

检查TLS指纹

TLS指纹也是一种浏览器完整性检查方法,不过需要收集来自非真实浏览器的TLS指纹做黑名单,或来自真实真实浏览器的TLS指纹做黑名单,两个都不简单,故此不是很推荐常规使用。

评论区

评论加载中