1 背景
研究人員開發者使用了一種檢測方法,並提供了檢測頁面對chrome瀏覽器進行檢測,
判斷是否是無頭瀏覽器。具體返回結果如下圖。
2 檢測手段分析
常見的前端檢測手段即通過前端JS腳本收集數據上報,然後檢測瀏覽器特徵判斷是否是無頭瀏覽器。分析過程即看請求中使用的分析腳本及上報數據。首先打開正常瀏覽器可以看到如下7個請求。
https://arh.antoinevastel.com/bots/scannerareuhead 發出的post請求會上報設備信息,如下爲截取部分結果。
{
"plugins":[
"Chrome PDF Plugin::Portable Document Format::internal-pdf-viewer::__application/x-google-chrome-pdf~pdf~Portable Document Format",
"Chrome PDF Viewer::::mhjfbmdgcfjbbpaeojofohoefgiehjai::__application/pdf~pdf~",
"Native Client::::internal-nacl-plugin::__application/x-nacl~~Native Client Executable,application/x-pnacl~~Portable Native Client Executable"
],
"mimeTypes":[
"~~application/pdf~~pdf",
"Portable Document Format~~application/x-google-chrome-pdf~~pdf",
"Native Client Executable~~application/x-nacl~~",
"Portable Native Client Executable~~application/x-pnacl~~"
],
"userAgent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3902.4 Safari/537.36",
"byteLength":1,
"gamut":[
true,
true,
false,
true
],
"anyPointer":"fine",
"anyHover":"hover",
"appVersion":"5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3902.4 Safari/537.36",
"appName":"Netscape",
"appCodeName":"Mozilla",
"onLine":true,
"cookieEnabled":true,
"doNotTrack":false
}
爲了繞過檢測,就是讓各種數據和真實設備一致。即發出的請求儘量完全一致(請求頭、請求體)。首先驗證設備參數的影響,從真實瀏覽器中提取設備數據存放在mockFinger文件中,當發送請求時將body替換觀察結果。
const fingerprint = JSON.parse(
fs.readFileSync("./mockFinger").toString()
);
const headers = {
'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3902.4 Safari/537.36'
};
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request', async interceptedRequest => {
const overrides = {
headers: {
...interceptedRequest.headers(),
...headers
}
};
if (interceptedRequest.url() === 'https://arh.antoinevastel.com/bots/scannerareuhead') {
fingerprint.uuid = await page.evaluate(() => uuid);
fingerprint.url = page.url();
const postData = JSON.stringify(fingerprint);
overrides.headers['content-length'] = postData.length;
overrides.postData = postData;
}
interceptedRequest.continue(overrides);
});
await page.goto(url);
發送請求後發現結果沒變化,頁面返回結果提示當前仍然是無頭瀏覽器。 由於請求除了body外,還有header等數據。因此嘗試修改header中的參數來觀察結果。
修改依然通過將真實設備數據複製過來觀察結果。直接修改header,在header中增加Accept-Language。再次運行頁面檢測提示當前瀏覽器不是無頭瀏覽器。嘗試將Accept-Language改爲小寫accept-language,發現服務端是對chrome瀏覽器的Accept-Language進行了存在性及大小寫驗證。調整後即可繞過檢測。
const puppeteer = require('puppeteer');
(async () => {
const puppeteer = require('puppeteer');
const browserOpts = {
headless: true,
args: ['--no-sandbox',
'--disable-setuid-sandbox',
'--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3902.4 Safari/537.36\',// \'Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0',
'--remote-debugging-port=9222'],
executablePath: "/XXX/XXX/XXX/XXX/chrome-mac/Chromium.app/Contents/MacOS/Chromium"
};
const browser = await puppeteer.launch(browserOpts);
const page = await browser.newPage();
await page.setRequestInterception(true);
page.on('request',(interceptRequest) => {
const headers = Object.assign({},
interceptRequest.headers(),{
'Accept-Language': 'zh-CN,zh;q=0.5'
});
interceptRequest.continue({headers});
});
await page.goto('https://arh.antoinevastel.com/bots/areyouheadless');
await browser.close();
})();
除了通過攔截的方式修改header,也可以在args參數中指定命令行參數設置語言選項 ‘–lang=zh-CN,zh;q=0.5’。而通過setExtraHTTPHeaders方法會將大寫變成小寫,導致無法通過檢測。
3 總結
檢測分析通常使用腳本收集瀏覽器特徵來分析。但訪問一個網站還會發出很多請求,因此除了腳本上報特徵也需要關注請求數據。模擬設備與真實設備依然存在微小的差異點。因此在反爬工作與爬蟲工作中應從訪問的流程上全面考慮可獲取到的信息,並通過分析系統分析。
4 參考
[1]對象的簡潔表示,http://es6.ruanyifeng.com/#docs/object
[2]chrome命令行參數,https://peter.sh/experiments/chromium-command-line-switches/
[3]are you headless分析,https://news.ycombinator.com/item?id=20479015
[4]request.continue方法,https://github.com/GoogleChrome/puppeteer/blob/v1.19.0/docs/api.md#requestcontinueoverrides