Cloudflare 作为全球最大的 CDN 和安全服务提供商之一,其反爬虫机制被广泛应用于各大网站。对于数据采集、自动化测试等场景,理解并绕过 Cloudflare 的防护成为一项重要技术挑战。
本文将从攻防双方视角,深入分析 Cloudflare 的反爬虫机制,以及浏览器指纹模拟的技术原理与实践。
Cloudflare 采用多层防护策略:
┌─────────────────────────────────────────────────────────────┐ │ 用户请求 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Layer 1: IP 信誉检查 │ │ - IP 黑名单检测 │ │ - 数据中心 IP 识别 │ │ - 地理位置异常检测 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Layer 2: HTTP 指纹检测 │ │ - User-Agent 验证 │ │ - HTTP 头顺序检查 │ │ - TLS 指纹 (JA3) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Layer 3: JavaScript 挑战 │ │ - 浏览器环境检测 │ │ - 指纹计算 │ │ - 行为分析 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Layer 4: CAPTCHA 验证 │ │ - hCaptcha / reCAPTCHA │ │ - 行为验证 │ └─────────────────────────────────────────────────────────────┘
当请求触发 JavaScript 挑战时,Cloudflare 会:
cf_clearance cookie挑战代码会检测:
javascript// 常见检测项
const navigatorFingerprint = {
userAgent: navigator.userAgent,
platform: navigator.platform,
language: navigator.language,
languages: navigator.languages,
hardwareConcurrency: navigator.hardwareConcurrency,
deviceMemory: navigator.deviceMemory,
maxTouchPoints: navigator.maxTouchPoints,
cookiesEnabled: navigator.cookieEnabled,
doNotTrack: navigator.doNotTrack,
webdriver: navigator.webdriver // 关键检测点
};
javascriptconst screenFingerprint = {
width: screen.width,
height: screen.height,
availWidth: screen.availWidth,
availHeight: screen.availHeight,
colorDepth: screen.colorDepth,
pixelDepth: screen.pixelDepth,
devicePixelRatio: window.devicePixelRatio
};
javascript// Canvas 指纹计算
function getCanvasFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// 绘制特定图案
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillStyle = '#f60';
ctx.fillRect(125, 1, 62, 20);
ctx.fillStyle = '#069';
ctx.fillText('Browser Fingerprint', 2, 15);
// 返回 hash
return canvas.toDataURL();
}
javascriptfunction getWebGLFingerprint() {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl');
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
return {
vendor: gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL),
renderer: gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL),
extensions: gl.getSupportedExtensions(),
parameters: {
maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
maxViewportDims: gl.getParameter(gl.MAX_VIEWPORT_DIMS)
}
};
}
javascriptfunction getAudioFingerprint() {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const analyser = audioContext.createAnalyser();
const gain = audioContext.createGain();
oscillator.type = 'triangle';
oscillator.frequency.setValueAtTime(10000, audioContext.currentTime);
gain.gain.setValueAtTime(0, audioContext.currentTime);
oscillator.connect(analyser);
analyser.connect(gain);
gain.connect(audioContext.destination);
oscillator.start(0);
oscillator.stop(0.1);
return analyser.frequencyBinCount;
}
使用 Playwright Stealth 插件:
pythonfrom playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
def create_stealth_browser():
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# 应用 stealth 模式
stealth_sync(page)
# 自定义 navigator 属性
page.evaluate('''() => {
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});
Object.defineProperty(navigator, 'platform', {
get: () => 'Win32'
});
Object.defineProperty(navigator, 'hardwareConcurrency', {
get: () => 8
});
}''')
return page
javascript// 添加随机噪声到 Canvas
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;
HTMLCanvasElement.prototype.toDataURL = function(type) {
// 在图像数据中添加噪声
const context = this.getContext('2d');
const imageData = context.getImageData(0, 0, this.width, this.height);
for (let i = 0; i < imageData.data.length; i += 4) {
// 添加随机噪声
imageData.data[i] += Math.floor(Math.random() * 10) - 5;
}
context.putImageData(imageData, 0, 0);
return originalToDataURL.apply(this, arguments);
};
javascript// 模拟真实 GPU 信息
const getParameterProxyHandler = {
apply: function(target, thisArg, args) {
const param = args[0];
const debugInfo = thisArg.getExtension('WEBGL_debug_renderer_info');
if (param === debugInfo.UNMASKED_VENDOR_WEBGL) {
return 'NVIDIA Corporation';
}
if (param === debugInfo.UNMASKED_RENDERER_WEBGL) {
return 'NVIDIA GeForce RTX 3080/PCIe/SSE2';
}
return target.apply(thisArg, args);
}
};
WebGLRenderingContext.prototype.getParameter = new Proxy(
WebGLRenderingContext.prototype.getParameter,
getParameterProxyHandler
);
javascript// AudioContext 噪声
const originalCreateAnalyser = AudioContext.prototype.createAnalyser;
AudioContext.prototype.createAnalyser = function() {
const analyser = originalCreateAnalyser.apply(this, arguments);
// 添加微小噪声
const originalGetFloatFrequencyData = analyser.getFloatFrequencyData.bind(analyser);
analyser.getFloatFrequencyData = function(array) {
originalGetFloatFrequencyData(array);
for (let i = 0; i < array.length; i++) {
array[i] += Math.random() * 0.0001 - 0.00005;
}
};
return analyser;
};
pythondef simulate_human_mouse(page, target_x, target_y):
"""模拟人类鼠标移动"""
import random
import math
current = page.evaluate('() => ({x: window.mouseX || 0, y: window.mouseY || 0})')
start_x, start_y = current['x'], current['y']
# 生成贝塞尔曲线控制点
steps = random.randint(20, 40)
points = []
for i in range(steps + 1):
t = i / steps
# 添加随机偏移
noise_x = random.gauss(0, 2)
noise_y = random.gauss(0, 2)
x = start_x + (target_x - start_x) * t + noise_x
y = start_y + (target_y - start_y) * t + noise_y
points.append((x, y))
# 移动鼠标
for x, y in points:
page.mouse.move(x, y)
time.sleep(random.uniform(0.01, 0.03))
pythondef simulate_scroll(page, distance):
"""模拟人类滚动"""
import random
remaining = distance
while remaining > 0:
# 随机滚动量
scroll = min(remaining, random.randint(50, 150))
page.evaluate(f'window.scrollBy(0, {scroll})')
remaining -= scroll
# 随机延迟
time.sleep(random.uniform(0.05, 0.2))
JA3 是一种 TLS 客户端指纹技术,通过以下字段生成 hash:
pythonimport subprocess
def make_request_with_ja3(url, browser='chrome110'):
"""使用 curl-impersonate 模拟浏览器 JA3"""
result = subprocess.run(
['curl-impersonate-' + browser, '-s', url],
capture_output=True,
text=True
)
return result.stdout
pythonimport undetected_chromedriver as uc
def create_undetected_driver():
"""创建不被检测的 Chrome"""
options = uc.ChromeOptions()
options.add_argument('--headless')
options.add_argument('--no-sandbox')
driver = uc.Chrome(options=options, version_main=None)
# 自动处理 Cloudflare 挑战
return driver
pythonfrom playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
with sync_playwright() as p:
browser = p.chromium.launch(headless=True)
page = browser.new_page()
# 应用所有 stealth 补丁
stealth_sync(page)
# 访问受保护页面
page.goto('https://example.com')
# 等待 Cloudflare 挑战完成
page.wait_for_selector('#content', timeout=30000)
javascriptconst puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
async function browse() {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox']
});
const page = await browser.newPage();
// Stealth 插件自动处理大部分检测
await page.goto('https://example.com');
// 等待通过 Cloudflare
await page.waitForTimeout(5000);
}
pythonfrom playwright.sync_api import sync_playwright
from playwright_stealth import stealth_sync
import time
def bypass_cloudflare(url):
with sync_playwright() as p:
browser = p.chromium.launch(headless=False)
context = browser.new_context(
user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
)
page = context.new_page()
# 应用 stealth
stealth_sync(page)
# 模拟人类行为
page.goto(url)
# 等待挑战完成
try:
page.wait_for_selector('.challenge-running', state='hidden', timeout=30000)
except:
pass
# 模拟滚动
page.evaluate('window.scrollBy(0, 300)')
time.sleep(1)
# 获取内容
content = page.content()
browser.close()
return content
pythonimport asyncio
from playwright.async_api import async_playwright
from playwright_stealth import stealth_async
async def scrape_page(browser, url):
page = await browser.new_page()
await stealth_async(page)
await page.goto(url)
await asyncio.sleep(2) # 等待挑战
content = await page.content()
await page.close()
return content
async def main(urls):
async with async_playwright() as p:
browser = await p.chromium.launch(headless=True)
tasks = [scrape_page(browser, url) for url in urls]
results = await asyncio.gather(*tasks)
await browser.close()
return results
Cloudflare 反爬虫与浏览器指纹模拟是一场持续的攻防博弈:
本文作者:yowayimono
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!