它是*不*可能检测和阻止铬headless

通过Evan Sangaline.|2018年1月18日

几个月后,我写了一篇名叫的流行文章使无头铬探测不到响应一个调用检测镀铬无头通过抗翁超薄。我真正试图以书面形式讨论的一件事是,基于浏览器指纹识别的阻止网站访问者是一个极其敌对的惯例。浏览器配置中简单的变化简单,您不可避免地将最终阻止对您的网站的无自动访问,而且 - 在封锁复杂的Web刮板方面,您真的没有完成任何内容。为了说明这一点,我展示了如何绕过Antione的第一篇文章中的所有建议的“测试”并指出他们尚未在多个浏览器版本中进行测试,并且对任何具有Beta或不稳定的Chrome构建的用户都会失败。

那些测试版和不稳定版已经变成了稳定版,安提龙也发布了一个更新版的博文可以检测和阻止镀铬无头他删除了我指出的依赖于版本的测试,并添加了一些新测试。你们可能已经从书名中猜到了这篇文章,我倾向于不同意他的标题。澄清:我不认为可以检测试图隐藏它的用户的浏览器自动化,如果他们不是试图隐藏它,然后它是微不足道的。

有一个window.navigator.webdriver属性的一部分WebDriver规范这意味着何时使用WebDriver时,但它在Chrome中实现了指示任何浏览器自动化。属性的摘要镀铬意图发货是这样的。

添加枚举,不可配置的readonly属性webdriver窗口全局对象的Navigator对象。属性是真正如果CommandLine具有“启用自动化”或“无头”开关。否则是

所以基本上,const以IsAutomated = Navigator.WebDriver;是你无头的测试(好的,真正的测试自动化而不是无头浏览器,但这通常是人们试图做的事情)。一旦你超越了这一点,你基本上就表明你在尝试检测那些主动隐藏他们的浏览器是自动的事实的用户。那就是不可能的时候。您可以提出任何您想要的测试,但是任何专门的web scraper都可以轻松地绕过它们。

如果你读过使无头铬探测不到,那篇文章应该非常熟悉。我将设置一个测试页,实现每个ontone的测试,然后展示绕过每个人的方式如何微不足道。我用了上次,所以这次我用傀儡给它加点香料。大部分的测试绕过都是通过JavaScript注入实现的,但是,这么多的代码应该适用于任何支持JavaScript注入的浏览器自动化框架(见:JavaScript注射注射硒,木偶仪和Marionette)。这里开发的所有代码——以及运行它的指令——也是如此GitHub上可用。如果您想尝试代码并在此处阅读时进行实验,请首先抓住这一点。

执行测试

我在一个名为的简单测试页面中实现了ontione提出的每个测试Chrome-Hastless-Test.html。页面呈现像这样的简单表

初始无头测试结果。

与每个测试的结果。页面加载时用于填充表的实际测试Chrome-Walless-Test.js,但是当我覆盖绕过它们时,我会覆盖每个测试的细节。这里显示的结果是无头浏览器产生的,而无需隐藏自己的尝试。

正如我之前提到的那样,我会使用傀儡作为我整个文章的自动化框架。木偶仪提供友好的高级JavaScript API,基于顶部Chrome DevTools协议,它是我最喜欢使用的自动化框架之一。可以通过运行安装纱线安装木偶尔它会下载自己的Chromium版本node_modules在自动化过程中使用。

访问测试页并将测试结果与Puppeteer一起录制测试结果非常简单。我们基本上刚刚在无头模式下启动浏览器,访问测试页,拍摄结果表的屏幕截图,然后退出。执行此操作的代码可用test-hastless-initial.js,其内容如下。

//我们将使用木偶尔是我们的浏览器自动化框架。Const Puppeteer =要求('木偶');//这是我们将把代码绕过测试的地方。const prepareepagefortests =异步(页面)=> {// todo:尚未实现。}(async()=> {//以无头模式启动浏览器并设置页面。const browser = await puppeteer.launch({args:['--no-sandbox'],无头:true,});const page = await browser.newpage(); //准备测试(尚未实施)。等待PrepareEpageForts(页面); //导航到将执行测试的页面。const testull ='https://intoli。万博输10万怎么办COM / BLOG /'+'不可能块 - 块 -  CHROME-HAST-TESH-TEST.HTML'; AWAIT PAGE.GOTO(TESTURL); //保存结果的屏幕截图。等待Page.screenshot({PATH:'HASELESS-TEST-RESULT.png'}); //清理。等待浏览器.CLOSE()})();

这可以运行节点test-headless-initial.js它会生成a无头测试结果.png文件与我们之前看到的结果表。

你会注意到有一个准备好在文件顶部定义的方法,当前没有任何作用。在本文的其余部分中,我们将逐一完成测试并显示如何绕过它们。最终结果可用test-hastless-final.js如果您想立即查看所有代码。

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

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

绕过测试

现在让我们一个一个地检查并绕过这些测试。请记住,在每个部分中开发的所有代码都打算进入准备好在访问测试页之前调用的方法。这意味着页面对象可用,我们在内部异步功能(所以我们可以使用等待)。如果你对任何零件如何合适的困惑,请看看test-hastless-final.js

切换测试

//用户 - 代理测试if(/headlesschrome/.test(nugigator.useragent)){//测试失败...}

这是自动访问自动访问的经典测试,此后一直存在于无头浏览器或甚至是浏览器自动化框架(至少用于服务器端检查)。它具有相对较低的假期率,但它也是欺骗或旁路最琐碎的事情。命令行工具如卷曲w提供旗帜改变用户代理标题,Chrome没有什么不同。您可以指定- 用户代理启动Chrome-Wallless或其他时的标志 - 它将修改两个用户代理标题和navigator.userAgent对象。

木偶师还提供了一个setuseragent()可以用来完成同样事情的方法。它所需要的只是增加

//通过用户代理测试。const userAgent = 'Mozilla/5.0 (X11;Linux x86_64)' + 'AppleWebKit/537.36 (KHTML,像Gecko) Chrome/64.0.3282.39 Safari/537.36';等待page.setUserAgent (userAgent);

对我们preparePageForTests ()方法,此测试通过。

Webdriver测试

// WebDriver测试(Navigator.Webdriver){//测试失败...}

这是几个原因的有趣测试。这是一件事的唯一其他测试,对一件事有相对较低的假阳性率(尽管存在依赖浏览器自动化的可访问性工具)。此外,如果您尝试设置,您会看到一些奇怪的行为navigator.webdriver= false作为绕过测试的一种方法。在分配任务时不会出现错误,但是navigator.webdriver仍将是真正之后。

要解决这个问题,我们需要使用Object.DefineProperty()重新定义webdriver属性导航器使用新的getter函数。这是在Chrome DevTools中交互方式执行此操作的示例。

覆盖WebDriver.

添加到我们的相关代码preparePageForTests ()方法是

//通过网络驱动程序测试。evaluateonnewdocument () => {Object.defineProperty(navigator, 'webdriver', {get: () => false,});});

它为我们带来了一步,因为我们完全没有无头考试。请注意,我们正在使用木偶尔evaluateOnnewdocument()方法以确保在页面上下文中重新定义我们的属性在任何页面的JavaScript可以运行之前重新定义。我们也将在所有其他测试中使用相同的方法。您可以了解有关IT的更多详细信息以及我们的其他浏览器自动化框架的类似方法JavaScript注射注射硒,木偶仪和Marionette文章。

Chrome测试

// Chrome测试if(!window.chrome){//测试失败...}

谷歌Chrome目前不支持扩展,窗口对象在无头模式下未定义(注意:我弄错了关于本文早期版本未定义的属性)。在非无头浏览器中,窗口对象看起来像这样的东西(请记住,页面上下文中不可用Web Extensions API)。

{应用:{isInstalled:假的,},webstore: {onDownloadProgress onInstallStageChanged:{}:{},},运行时:{PlatformOs: {MAC:“苹果”,赢得:“赢”,ANDROID:“安卓”,横:“横”,LINUX:“LINUX”,OPENBSD: OPENBSD,}, PlatformArch:{手臂:“手臂”,X86_32: x86-32, X86_64:“x86 - 64”,},PlatformNaclArch:{手臂:“手臂”,X86_32: x86-32, X86_64:“x86 - 64”,},RequestUpdateCheckStatus:{扼杀了:“压制”,NO_UPDATE: NO_UPDATE, UPDATE_AVAILABLE: UPDATE_AVAILABLE,}, OnInstalledReason:{INSTALL: ' INSTALL ', UPDATE: ' UPDATE ', CHROME_UPDATE: ' CHROME_UPDATE ', SHARED_MODULE_UPDATE: ' SHARED_MODULE_UPDATE ',}, OnRestartRequiredReason: {APP_UPDATE: ' APP_UPDATE ', OS_UPDATE: ' OS_UPDATE ',周期:'周期',},},}

在此基础上,可以进行更复杂的测试,深入了解系统的结构对象。

// Chrome测试if(!window.chrome ||!window.chrome.runtime){//测试失败...}

当然,这仍然可以绕过绕过。我们只需要通过测试代码检查的任何东西。

//通过铬测试。evaluateonnewdocument() =>{//我们可以根据测试需要对其进行任意深度的模拟。window.navigator。铬= { runtime: {}, // etc. }; });

流行,在preparePageForTests ()你很高兴去。

测试的权限

//权限测试(async()=> {const permissionstatus = await navigator.permissions.query({name:'notifications'});如果(notification.permission ==='拒绝'&& permissionStatus.state ==='提示'){//测试失败...}})();

这个测试背后的想法是Notification.permission结果是Navigator.Permissions.Query.报告矛盾值。我们需要做的只是为了确保它们是一致的。我们可以通过推翻来实现这一点Navigator.Permissions.Query.一种行为我们想要它的方法。

//通过权限测试。evaluateonnewdocument () => {const originalQuery = window.navigator.permission .query;window.navigator.permissions返回。查询=(参数)=> (parameters .name === 'notifications' ?的承诺。解决({状态:通知。权限}):originalQuery(参数));});

请注意,这将通过原件Navigator.Permissions.Query.如果查询名称不是,则调用通知,但是如果测试更复杂,则可以类似地处理其他查询。

插件长度测试

//插件长度测试if(navigator.plugins.length === 0){pluginslendglement.classlist.add('failed');//测试失败...}

这是一个特别是用户敌对的测试,因为浏览器插件通常被隐藏为隐私度测量。Firefox默认情况下,有延期在Chrome中提供相同的行为。为了欺骗插件,我们可以简单地在上定义另一个属性getter导航器就像我们为webdriver

//通过插件长度测试。{//重写' plugins '属性以使用自定义getter。对于当前的测试,这只需要有' length > ',但是如果需要,我们也可以模拟插件。get: () => [1,2,3,4,5],});});

语言测试

//语言测试(!Navigator.Languages || Navigator.Languages.Length === 0){//测试失败...}

最后,我们进入语言测试。这是另一个测试,香草无头铬通过没有问题,为我。如果不深入研究这一点,它可能取决于操作系统或其配置。

如果您的配置不提供任何语言Navigator.languages.,您可以再次定义属性getter,就像我们对两个属性都做的那样插件webdriver

//通过语言测试。await page.evaluateOnnewdocument(()=> {//覆盖`plugins`属性使用自定义getter。object.defineproperty(Navigator,'语言',{GET :()=> ['en-Us','en''],});});

这是最后一个测试,一切都应该通过。一定要结账离开test-hastless-final.js看看一切如何适合或尝试为自己运行它。

把它整合在一起

绕过测试后的最终结果

正如您所看到的,我们能够绕过所有这些测试,无需很多困难。我们曾经绕过这些的脚本显然不会在所有情况下工作,但这些技术通常适用,可以为网站可能采用的任何特定测试集进行自定义。如果有人想要隐瞒他们网站上使用浏览器自动化框架的事实,那么他们将能够。基于浏览器指纹识别的阻止用户通常是一个坏主意,而且主要最终会伤害用户。

如果您发现这篇文章是因为您自己正在处理一些浏览器自动化问题,那么请这样做保持联系。我们对这种事情有很多经验,我们喜欢在新项目上工作。

建议的文章

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

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

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

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

阅读更多

打破Chrome / Webextension Sandbox

通过Evan Sangaline.
2018年9月14日

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

阅读更多

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

通过Evan Sangaline.
2018年8月30日

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

阅读更多

评论