从.Net v4开始,直到现在(我已经对4.7.2进行了测试),Request.Browser(HttpBrowserCapabilities对象)中可访问的内置浏览器检测功能存在严重缺陷,一旦您的站点被您咬伤,每分钟吸引几个访客。
如果您依赖于此对象,则发生的情况是用户将随机报告其浏览器被错误标识。
这篇文章显示了为什么会发生以及如何解决它。这个问题也很容易在本地复制,值得花几分钟的时间。如果您愿意,则该问题也很容易导致DoS攻击。
我们的网站依赖于某些更高版本的浏览器功能,因此我们绝对需要这些功能,因此我们使用浏览器检测功能告知使用旧版浏览器的用户进行升级。它可以正常工作一会儿,然后突然告诉使用最新浏览器版本的用户需要升级。一旦发生这种情况,通常会持续一天的剩余时间。
要进行重现,请在.aspx文件中使用以下代码创建一个asp.net项目(网络表单,MVC并不重要):
在本地或服务器上访问此页面,您将获得Chrome v88。到现在为止还挺好。
浏览器是根据User-Agent HTTP标头分类的。我的Chrome发送此邮件:Mozilla / 5.0(Windows NT 10.0; Win64; x64)AppleWebKit / 537.36(KHTML,like Gecko)Chrome / 88.0.4324.104 Safari / 537.36
现在,快速将字符串中间的Chrome / 88更改为Chrome / 86(或任何其他数字),然后重试。例如在Curl中:
curl http:// localhost:58571 -H" User-Agent:Mozilla / 5.0(Windows NT 10.0; Win64; x64)AppleWebKit / 537.36(KHTML,like Gecko)Chrome / 86.0.4324.104 Safari / 537.36&#34 ;
还是v88?多么奇怪……让我们刷新几次。仍为v86。让我们考虑一分钟...嗯...再次刷新。走了!回到v88!
发生的情况是,从.Net v4开始,为了提高性能,浏览器分类的结果存储在以用户代理字符串为键的哈希表中。但是不是整个字符串,不是,那会为我们省去很多麻烦,不是,关键只是前64个字符(源代码行46)。
Chrome版本号位于位置90,因此根本不是键的一部分。更令人沮丧的是,缓存具有一个滑动窗口(源代码行247,304、369)1分钟到期(源代码行147)。这意味着:只要每分钟至少读取一次缓存键,它就不会消失。随之而来的是狂欢。
为什么是512?事实证明,.Net将User-Agent字符串的上限限制为512个字符,以避免资源受到攻击(源代码行188)。我想象微软的某个人决定512个字符对于任何人都应该足够了。
如果您像我们一样依靠浏览器标识来启用某些功能,则web.config中如果没有此行,则您的站点很容易受到DoS攻击。攻击者将等待低流量时间(通常是半夜)来发出前64个字节的请求,看起来像网络上最常用的User-Agent,而其余字符串则用低版本号或引导浏览器功能的其他内容认为“先生,这是一个较旧的浏览器,但它签出了”。
只要每分钟每个用户代理发出一个请求,这些请求就会被永久错误地识别。 一种低成本,省力的DoS攻击。