使无头铬探测不到

通过Evan Sangaline.| 2017年8月9日

检测Headles铬

一篇题为检测镀铬无头周末突然出现在黑客新闻中,自以来一直在进行。关于黑客新闻的大多数讨论都集中在作者的一些可疑断言,即Web删除是一个属于与广告欺诈和黑客网站相同的类别的“恶意任务”。这总是一个有趣的辩论,但我真正接受了这篇文章的事情是它隐含地推动了基于浏览器指纹阻止用户的想法。就我而言,这通常是一个可怕你更有可能阻止和挫败你的用户,而不是提供任何有意义的威慑那些你试图阻止。

为了说明这一点,我实现了中建议的所有测试检测镀铬无头而且,不出所料,我的标准日常浏览器失败了一些测试。

桌面测试结果

我向不同平台上的少数朋友发了同样的测试每一个人其中至少失败了。我们均无法访问Naïively实现拟议的测试集并基于它们阻止内容的网站。检查你自己看看你是否会被封锁。

如果你只是检查几个浏览器配置,然后在此基础上屏蔽内容,那么毫无疑问你会无意中屏蔽一些人。举个例子,其中一个被提出的测试——作者称之为他的“最有力的”测试——是破碎图像的尺寸不等于0x0.。此测试可能已在Chrome 59中工作,但它不再在Chrome 60中进行。要制作合法强大的东西,您将现实地需要为Chrome,Safari,Firefox,Chromium,Opera,Brave,等等而且您还需要测试是特定于特定的版本且不断更新的测试。

以远程负责任的方式实现这样的过滤需要真的知道你在做什么,它会涉及巨大的努力。和什么?无论如何,它不像任何这些测试都没有琐碎。您可能成功地阻止何施施胶因安全问题而禁用Chromium PDF插件,但任何人都对广告欺诈,Blackhat黑客或 -上帝保佑web抓取不会有任何困难绕过任何测试,你可以想出。它就像数字版权管理;您所能期望的最好结果是给您的用户提供普遍的更糟糕的体验,这样您就可以略微给那些无论如何都要避开任何威慑的人带来不便。

为了帮助强调这些测试在实际上实际上并不实际生效,我决定经过并展示他们中的每一个如何绕过。它只采取了几百条代码来使Chrome无头在测试上更好地做到比标准Chrome!这些当然不会使Chrome无头普遍无法察觉,但可以以类似的方式轻松绕过任何更复杂的测试。希望你能找到这里提出的技术有趣的是你是否正在做一些自己的网络刮擦或只是好奇,看看它是如何完成的。

用户代理

这几乎是唯一可以合法地识别Chrome无头的测试之一,但它也是绕过最微不足道的。默认用户代理在无头模式下运行Chrome时将是类似的

Mozilla / 5.0(X11; Linux X86_64)AppleWebkit / 537.36(如壁虎,如壁虎)摇摆不道/ 60.0.3112.50 Safari / 537.36

泄露信息的关键部分在哪里摇摆啰嗦。要改变这一点,我们可以简单地提供Chrome- 用户代理命令行选项。如果你直接从命令行运行Chrome,那么你只需要包括这个选项- 黑色一个。

/opt/chrome/chrome-beta/chrome -headless \ -user-agent="Mozilla/5.0 (X11;Linux x86_64) AppleWebKit/537.36 (KHTML,像Gecko) Chrome/60.0.3112.50 Safari/537.36”

可以使用相同的选项使用ChromeOptions.add_argument()如果你正在使用Python, Selenium和ChromeDriver。

来自Selenium Import Webdriver User_Agent ='Mozilla / 5.0(X11; Linux X86_64)AppleWebkit / 537.36(如壁虎)Chrome / 60.0.3112.50 Safari / 537.36'选项= webdriver.chromeOptions()#指定无头模式选项.add_argument('无头')#指定所需的用户代理options.add_argument(f'user-agent = {user_agent}')驱动程序= webdriver.chrome(chrome_options =选项)#用户 - 代理现已设置

如果您想使用该方法略有不同Chrome DevTools协议,但这与实现的同样微不足道。

require('chrome-remote-interface');const useragent ='mozilla / 5.0(x11; linux x86_64)AppleWebkit / 537.36(如壁虎)Chrome / 60.0.3112.50 Safari / 537.32.50 Safari / 537.36'CDP(异步函数(客户端){const {network,page} = client; await页面.bable(); await network.enable();等待Network.setUserAgentOverride({UserAgent}); //用户代理现在设置};

所有这些方法都将更改HTTP报头和HTTP代理中的用户代理window.navigator.useragent.。现在来看更具挑战性的部分!

语言和插件

拟议的两个测试是检查navigator.plugins。长度= = = 0Navigator.languages ==''。这些都可以通过将JavaScript注入覆盖的每个页面来绕过window.navigator.具有所需的值。让我们专注于JavaScript现在嘲笑这些值。我们将循环回来查看一旦我们通过所有测试,就如何注射它,因为所有这些都需要立即注射。

我们基本上只想覆盖插件语言属性导航器具有传递测试的值。您的第一个想法可能是直接设置物业

Navigator.languages.= ['en-US', 'en']; navigator.plugins = [1, 2, 3, 4, 5];

但这实际上并不覆盖价值,因为这些是仅具有Getter函数作为访问者的自动的属性。我们需要相反使用Object.DefineProperty()使用新的getter函数重新定义属性。可以这样做。

//覆盖'languages'属性来使用一个定制的getter对象。defineproperty (navigator, 'languages', {get: function() {return ['en- us ', 'en'];}});//重写'plugins'属性来使用一个定制的getter对象。defineproperty (navigator, 'plugins', {get: function(){//这只需要有' length > ',但是我们可以模拟插件返回[1,2,3,4,5];}});

在页面上执行此JavaScript后,Navigator.languages.将报告['en-us','en']navigator.plugins会有一个长度5.。插件可以更深入地嘲笑,但在这种情况下,这不是必需的,因为我们绕过的测试只检查阵列的长度。

万博输10万怎么办Intoli智能代理

如果您正在进行严重的web抓取,那么使用代理是必须的。我们的万博输10万怎么办Intoli智能代理服务使它很容易停止被僵尸缓解服务阻塞。您的请求被智能地路由到可能成功的本地ip,失败的请求被自动重试,您甚至可以通过远程浏览器发出请求,预加载自定义使它很难检测到您的刮刀。在下面输入您的电子邮件地址,以获得相同的工具,我们使用所有我们自己的网络刮!

WebGL供应商和渲染器

下一个测试创建WebGL上下文,然后检查供应商和渲染器字符串。一串供应商布莱恩·保罗或渲染器Mesa offscreen.应该表明Chrome无头。我无法在我的机器上重现这些字符串,但让我们来看看我们如何嘲笑这些值,以防他们存在。我们将简单地硬码WebGLRenderingContext.getParameter ()通过修改原型来返回所需的供应商和渲染器字符串WebGlRenderingContext.。修改原型确保将在任何情况下调用我们修补的版本wegglrenderingcontext.是创造的(例如通过执行canvas.getContext('WebGL'))。

const getParameter = WebGlRenderingContext.getParameter;webglrenderingcontext.prototype.getparameter =函数(参数){// unpasked_vendor_webgl if(参数=== 37445){返回'英特尔开源技术中心';} // ovasked_RENDERER_WEBGL IF(参数=== 37446){返回'MESA DRI INTEL(R)Ivybridge Mobile';}返回getParameter(参数);};

这个补丁版本的GetParameter()返回渲染器和供应商的所需值,同时推迟到任何其他参数值的标准实现。这里的整数是标准常数哪个唯一标识参数。

破碎的图像

我们已经提到了破碎的图像测试并没有真正有意义,因为Chrome 60报告了图像大小0x0.对于破碎的图像。让我们来看看我们如何做到这一点,只是为了表明它也很容易绕过。这种方法结合了我们已经使用的几种技术。

我们再次修改原型,这次htmlimageelement.,因此我们制作的更改将适用于DOM中创建的任何图像。都宽度高度属性有访问器,所以我们也需要使用Object.DefineProperty()覆盖他们的吸气剂方法。我们将返回宽度和高度20.对于破碎的图像和其他简单地遵循标准的getter实现。

['高度','宽']。foreach(property => {//存储现有描述符const imagedescriptor = object.getownpropertydescriptor(htmlimageelement.prototype,属性);使用修补的描述符对象重新定义属性.defineproperty(htmlimageElement.prototype,属性,{... imagedescriptor,get:function(){//如果图像无法加载,则返回任意的非零维度(这个.pplete && this.naturalheight == 0){return 20;}//否则,返回实际维度返回imagedescriptor.get.apply(此);},});});

这比一些早期的复杂更复杂,但它仍然相当简单。

Retina / Hidpi发际线功能

最后一种方法是利用该方法检测视网膜发际线的支持度Modernizr图书馆。这是另一个测试,因为大多数人没有HIDPI屏幕,大多数用户的浏览器都不支持此功能。然而,即使使用作为测试也是有意义的,它会绕过绕过。

我们可以从现代资源来源,测试基本上是插入adiv标签与id现代人进入页面以及以下样式表。

#modernizr {border:.5px solid透明;}

offsetHeight财产的div然后检查,如果其值为1然后支持发际线功能。我们所需要的只是修改原型htmldevelement.这样offsetHeight回报1如果id是现代人。这个基本模式现在应该很熟悉了……

//存储现有的描述符const元素intelstrement r = object.getownpropertydescriptor(htmlelement.prototype,'offsetheight');//用修补的描述符对象重新定义属性.defineproperty(htmldiveLement.prototype,'offsetheight',{... ompentDescriptor,get:clication(){if(this.id ==='modernizr'){return 1;}return exporteDescriptor.get.apply(此);},});

面对网上刮擦的独特挑战?

我们将与您合作以构建定制解决方案以满足您的需求。从Web Scraping到机器学习,我们的专家都在这里提供帮助。

现在开始

把它整合在一起

此时,我们有一堆javascript片段,将绕过Chrome无头测试。我们只是需要Chrome在目标网站上的测试代码之前执行它们。可以使用呼叫将JavaScript注入硒的页面WebDriver.executeScript (),但遗憾的是,只有被称为文件负载事件发射了。这意味着,如果有一个同步测试阻塞了页面加载,那么它将在注入的JavaScript有机会模拟测试正在检查的各种事情之前运行。

如果我们想可靠地通过测试,那么我们需要找到一种方法来确保我们的代码在页面上的测试代码之前运行。我最喜欢的方法是写一个小的Chrome扩展注入脚本标签到任何已访问的页面。不幸的是,Chrome无头尚未支持扩展而且在可预见的未来可能不会。在添加该功能之前,我们需要使用其他方法。

将JavaScript注入页面的一种更强大的方式是实际修改所请求的HTML In-Flies中并注入脚本标签之前,浏览器曾经有机会看到原来的。这是一种技术,更常用来提供修补版本的网站自己的脚本,但它可以应用在相同的方式,这个使用。我们会使用mitmproxy,这是一个支持tls的HTTP代理,很容易编写脚本,以注入我们的代码。

首先,我们需要安装Mitmproxy和BeautifueSoup4。这些都可能通过系统的包管理器提供,但您也可以在VirtualEnv中安装它们pip安装mitmproxy bs4如果是这样的。现在,我们将创建一个简短的python脚本inject.py.具有以下内容。

从BS4导入BeautySoup从Mitmproxy导入CTX#加载到JavaScript中,以将打开('content.js','r')注入f:content_js = f.read()def响应(flow):#仅过程200响应html内容如果flow.response.headers ['content-type']!='text / html':return unf flow.response.status_code == 200:return#注入脚本标记html = beautifuesoup(flow.response.text,'lxml')container = html.head或html.body如果容器:script = html.new_tag('script',type ='text / javascript')script.string = content_js container.insert(0,脚本)流。response.text = str(html)ctx.log.info('成功注入content.js脚本')

inject.py.脚本定义了一个响应(流)在每个响应之前,Mitmproxy将调用的功能。如果响应有一个200.状态码和内容类型文字/ html.,然后注入脚本标签与加载的内容content.js.。的content.js.这里仅包含我们在前几节中开发的所有JavaScript测试旁路。

要使用此脚本启动代理,我们现在可以运行以下内容。

mitmdump -p 8080 -s“inject.py”

我们也必须告诉Chrome通过指定来使用我们的代理- 代理服务器选择。

/ opt / google / chrome-beta / chrome \ --headless \ --proxy-server = localhost:8080 \ --remote-debugging-port = 9222

- 黑色选项可预测地告诉Chrome以无头模式运行- remote-debugging-port指定我们将用于与实例通信和控制实例的调试接口。您现在可以运行该命令,它只坐下来等待我们在几分钟内连接到调试端口。

我们会使用Chrome远程接口来实际控制实例。这个界面有点类似于Selenium,但它是特定于Chrome的,允许对某些事情进行更细粒度的控制。特别是,它允许我们接受来自mitmproxy的自签名证书。这应该可能与ChromeDriver因为AcceptInsecureCerts.被指定为部分W3规格,但这个特殊的功能尚未在Chrome无头中实现。

要安装Chrome远程界面,可以运行纱线添加Chrome-Remote接口或与您选择的包管理器等效的内容。安装后,创建一个名为test-headless.js具有以下内容。

require('chrome-remote-interface');const fs = require('fs');//全局设置const filename = 'headless results.png';const url = 'https://万博输10万怎么办www.lvdunfc.com/blog/making-chrome-headless undetectable/chrome-headless test.html';const userAgent = 'Mozilla/5.0 (X11;Linux x86_64) AppleWebKit/537.36 (KHTML,像Gecko) Chrome/60.0.3112.50 Safari/537.36' CDP(async function(client) {const {Network, Page, Security} = client;等待Page.enable ();等待Network.enable ();等待Network.setUserAgentOverride ({userAgent});//忽略所有证书错误来支持mitmproxy证书等待安全。enable(); await Security.setOverrideCertificateErrors({override: true}); Security.certificateError(({eventId}) => { Security.handleCertificateError({ eventId, action: 'continue' }); }); // navigate to the page and wait for it to load await Page.navigate({url}); await Page.loadEventFired(); setTimeout(async function() { // save the screenshot const screenshot = await Page.captureScreenshot({format: 'png'}); const buffer = new Buffer(screenshot.data, 'base64'); fs.writeFile(filename, buffer, 'base64', function(err) { if (err) { console.error(`Error saving screenshot: ${err}`); } else { console.log(`"${filename}" written successfully.`); } client.close(); }); }, 1000); // 1 second delay for the tests to complete }).on('error', err => { console.error(`Error connecting to Chrome: ${err}`); });

此脚本将连接到我们当前运行的无头辐射实例,访问测试页,在1秒后保存屏幕截图,然后退出。Chrome本身已经运行,已经配置为通过Mitmproxy来代理所有内容。因此,Chrome渲染的页面应包括我们的旁路代码该文件的标签。

所有的碎片现在都到位了,我们现在可以运行脚本节点test-headless.js(注意,由于使用了异步/等待,这将要求节点版本至少为7.6)。当脚本运行时,我们应该看到类似于下面的输出mitmdump终端。

加载脚本:inject.py代理服务器侦听http://0.0.0.0:8080 127.0.0.1:57132:clientdisconnect 127.0.0.1:59524:clientconnect 127.0.0.1:59524:connect Intoli.com:443 <<无法建立TLS万博输10万怎么办与客户端(SNI:Intoli.co万博输10万怎么办m):tlsexception(104,'econnreset')“,)127.0.0.1:59526:ClientConnect 127.0.0.1:59524:ClientDisconnect成功注入了Content.js脚本。127.0.0.1:59526:获取https://int万博输10万怎么办oli.com/blog/making-chrome-headless-undetectable/chrome-headless-test.html << 200 ok 1.12k 127.0.0.1:59528:clientconnect 127.0.0.1:59526:get https://i万博输10万怎么办ntoli.com/blog/making-chrome-headless-undetectable/modernizr.js << 200 ok 2.43k 127.0.0.1:59528:connect Intoli.com:443 <<无法与客户建立TLS(SNI:I万博输10万怎么办ntoli.com):tlsexception(“( -  1,'意外的eof')”,)127.0.0.1:59530:clientconnect 127.0.0.1:59528:clientdisconnect 127.0.0.1:59526:get //www.lvdunfc.com/博客/制作Chrome-Walless-undeterable / chrome-hassless-test.js << 200 ok 2.27k 127.0.0.1:59526:get https://intoli.c万博输10万怎么办om/nonexistentent -image.png << 404未找到189b

还有许多错误,但这些不担心,因为它们是客户需要覆盖证书错误的结果。忽略这些,看起来我们看到了预期的请求,并且在返回之前成功注入脚本标记Chrome-Hastless-Test.html响应。

最后,让我们看一下生成的headless-results.png验证我们现在通过所有测试。

无头的结果

我们成功地通过了考试!

总结

这是允许绕过测试的一点绕过测试的。简单地修补网站的检测脚本通常会更容易地传递,然后通过Mitmproxy服务修补版本。我采取了这一较长的路线的原因是我想真的强调,测试自己没有检查任何不容易欺骗的东西。

根本无法从人类用户区分乖巧的机器人。你为什么需要?如果BOT代表用户访问网站,并以与用户的相同速率浏览,那么差异确实有什么差异?我可以通过滥用访问完全理解阻塞用户,但它在风车上倾斜以试图限制任何自动访问的形式。您将尽最终阻止无辜的用户,智能机器人将未被发现。

如果您正在尝试刮掉网站,那么它的阻塞机制有点过分,那么请做随时与我们联系。万博输10万怎么办Intoli的团队成员是编写行为良好的机器人的专家,这些机器人与人类难以区分。我们还可以帮助您将自定义数据验证和分析工作流放置在适当的位置,以帮助您从数据中提取值。

建议的文章

如果你喜欢这篇文章,那么你也会喜欢这些相关的文章。

用Aopic算法执行有效的广泛爬网

通过安德烈Perunicic
2018年9月16日

了解如何在广泛的爬网中估算页面重要性并分配带宽。

阅读更多

打破Chrome / Webextension Sandbox

通过Evan Sangaline.
2018年9月14日

一个简短的指南打破webex张力的内容脚本沙箱。

阅读更多

用户代理 - 使用Google Analytics和Circleci生成随机用户代理

通过Evan Sangaline.
2018年8月30日

一个免费的数据集和JavaScript库,用于生成始终是当前的随机用户代理。

阅读更多

评论