一枚酸心果子

果子果子果子果子果子~~~

Web应用代理检测机制:前端与后端的攻防博弈

Web代理检测的技术背景

为什么Web应用需要检测代理?

Web应用检测代理的主要原因:

  • 反爬虫机制:防止自动化工具滥用API
  • 地理限制:确保用户来自特定地区
  • 安全防护:防止恶意流量和攻击
  • 数据统计:准确统计用户来源和访问量
  • 合规要求:满足某些法规对用户身份验证的要求

接下来我将通过一些代码片段来简单剖析其中的一些原理和应对方法

前端检测技术

1. JavaScript环境检测

浏览器指纹识别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 检测浏览器环境是否异常
function detectBrowserEnvironment() {
const checks = {
// 检测WebDriver
webdriver: navigator.webdriver === true,

// 检测自动化工具特征
automation: window.chrome && window.chrome.runtime && window.chrome.runtime.onConnect,

// 检测无头浏览器
headless: navigator.userAgent.includes('HeadlessChrome'),

// 检测Selenium
selenium: window.document.documentElement.getAttribute('selenium') !== null,

// 检测PhantomJS
phantom: window.callPhantom !== undefined,

// 检测Puppeteer
puppeteer: window.navigator.plugins.length === 0
};

return Object.values(checks).some(check => check === true);
}

// 检测代理相关特征
function detectProxyFeatures() {
const features = {
// 检测代理相关的HTTP头
proxyHeaders: detectProxyHeaders(),

// 检测网络延迟异常
networkLatency: detectNetworkAnomaly(),

// 检测IP地址特征
ipFeatures: detectIPFeatures()
};

return features;
}

网络环境检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
// 检测网络延迟和连接特征
function detectNetworkAnomaly() {
const startTime = performance.now();

// 创建测试请求
fetch('/api/ping', {
method: 'HEAD',
cache: 'no-cache'
}).then(() => {
const latency = performance.now() - startTime;

// 代理通常会增加延迟
if (latency > 1000) { // 超过1秒
console.log('检测到异常网络延迟,可能存在代理');
return true;
}

return false;
}).catch(() => {
return false;
});
}

// 检测WebRTC泄露真实IP
function detectWebRTCLeak() {
return new Promise((resolve) => {
const pc = new RTCPeerConnection({
iceServers: [{ urls: 'stun:stun.l.google.com:19302' }]
});

pc.createDataChannel('');
pc.createOffer().then(offer => pc.setLocalDescription(offer));

pc.onicecandidate = (ice) => {
if (ice.candidate) {
const candidate = ice.candidate.candidate;
// 检查是否泄露了真实IP
if (candidate.includes('192.168.') ||
candidate.includes('10.') ||
candidate.includes('172.')) {
console.log('WebRTC泄露真实IP,可能存在代理');
resolve(true);
}
}
};

setTimeout(() => resolve(false), 3000);
});
}

2. Canvas指纹检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Canvas指纹检测
function detectCanvasFingerprint() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');

// 绘制特定图形
ctx.textBaseline = 'top';
ctx.font = '14px Arial';
ctx.fillText('Canvas fingerprint test', 2, 2);

// 获取Canvas数据
const fingerprint = canvas.toDataURL();

// 检测指纹是否异常(代理工具可能修改Canvas渲染)
return analyzeCanvasFingerprint(fingerprint);
}

// 分析Canvas指纹
function analyzeCanvasFingerprint(fingerprint) {
// 检查指纹长度和特征
if (fingerprint.length < 100) {
return true; // 异常短的指纹
}

// 检查是否包含代理工具的特定特征
const proxySignatures = [
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==',
// 添加更多代理工具的签名
];

return proxySignatures.some(signature => fingerprint.includes(signature));
}

3. 时间检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 检测时间异常
function detectTimeAnomaly() {
const checks = {
// 检测系统时间是否被修改
systemTime: detectSystemTimeManipulation(),

// 检测时区是否异常
timezone: detectTimezoneAnomaly(),

// 检测时间精度
precision: detectTimePrecision()
};

return Object.values(checks).some(check => check === true);
}

// 检测系统时间是否被修改
function detectSystemTimeManipulation() {
const serverTime = getServerTime(); // 从服务器获取时间
const clientTime = new Date().getTime();
const diff = Math.abs(serverTime - clientTime);

// 如果时间差超过5分钟,可能存在时间修改
return diff > 5 * 60 * 1000;
}

// 检测时区异常
function detectTimezoneAnomaly() {
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
const offset = new Date().getTimezoneOffset();

// 检查时区是否与IP地理位置匹配
return checkTimezoneConsistency(timezone, offset);
}

后端检测技术

1. HTTP头检测

代理相关头部检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
from flask import Flask, request
import re

app = Flask(__name__)

def detect_proxy_headers():
"""检测代理相关的HTTP头"""
proxy_headers = {
'via': request.headers.get('Via'),
'x_forwarded_for': request.headers.get('X-Forwarded-For'),
'x_forwarded_proto': request.headers.get('X-Forwarded-Proto'),
'x_real_ip': request.headers.get('X-Real-IP'),
'x_forwarded_host': request.headers.get('X-Forwarded-Host'),
'forwarded': request.headers.get('Forwarded'),
'client_ip': request.headers.get('Client-IP'),
'remote_addr': request.environ.get('REMOTE_ADDR')
}

# 检查是否有代理头
for header_name, header_value in proxy_headers.items():
if header_value:
print(f"检测到代理头 {header_name}: {header_value}")
return True

return False

def analyze_x_forwarded_for():
"""分析X-Forwarded-For头"""
xff = request.headers.get('X-Forwarded-For')
if not xff:
return False

# 解析IP列表
ips = [ip.strip() for ip in xff.split(',')]

# 检查IP数量(代理链)
if len(ips) > 2:
print(f"检测到代理链,IP数量: {len(ips)}")
return True

# 检查IP格式
for ip in ips:
if not is_valid_ip(ip):
print(f"检测到异常IP格式: {ip}")
return True

return False

def is_valid_ip(ip):
"""验证IP地址格式"""
pattern = r'^(\d{1,3}\.){3}\d{1,3}$'
return re.match(pattern, ip) is not None

用户代理检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def detect_suspicious_user_agent():
"""检测可疑的User-Agent"""
user_agent = request.headers.get('User-Agent', '')

# 检测自动化工具
automation_tools = [
'selenium', 'webdriver', 'phantomjs', 'puppeteer',
'headlesschrome', 'chromedriver', 'geckodriver'
]

for tool in automation_tools:
if tool.lower() in user_agent.lower():
print(f"检测到自动化工具: {tool}")
return True

# 检测代理工具
proxy_tools = [
'proxy', 'vpn', 'tunnel', 'socks'
]

for tool in proxy_tools:
if tool.lower() in user_agent.lower():
print(f"检测到代理工具: {tool}")
return True

return False

2. IP地址检测

IP地理位置检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import requests
import json

def detect_ip_geolocation():
"""检测IP地理位置"""
client_ip = get_client_ip()

# 使用IP地理位置服务
geo_services = [
'http://ip-api.com/json/{}',
'https://ipapi.co/{}/json/',
'https://freegeoip.app/json/{}'
]

for service_url in geo_services:
try:
response = requests.get(service_url.format(client_ip), timeout=5)
geo_data = response.json()

# 检查地理位置是否合理
if is_suspicious_location(geo_data):
print(f"检测到可疑地理位置: {geo_data}")
return True

except Exception as e:
print(f"地理位置检测失败: {e}")
continue

return False

def is_suspicious_location(geo_data):
"""判断地理位置是否可疑"""
# 检查是否来自数据中心
if geo_data.get('org', '').lower() in ['amazon', 'google', 'microsoft', 'digitalocean']:
return True

# 检查是否来自VPN/代理服务商
vpn_keywords = ['vpn', 'proxy', 'hosting', 'datacenter']
org = geo_data.get('org', '').lower()
for keyword in vpn_keywords:
if keyword in org:
return True

return False

IP黑名单检测

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def check_ip_blacklist():
"""检查IP黑名单"""
client_ip = get_client_ip()

# 代理IP数据库
proxy_ips = load_proxy_database()

if client_ip in proxy_ips:
print(f"IP在黑名单中: {client_ip}")
return True

# 检查IP段
for ip_range in proxy_ips:
if is_ip_in_range(client_ip, ip_range):
print(f"IP在代理IP段中: {client_ip}")
return True

return False

def load_proxy_database():
"""加载代理IP数据库"""
# 这里可以从文件或数据库加载代理IP列表
return [
'1.2.3.4',
'5.6.7.8',
# 更多代理IP...
]

3. 行为检测

访问模式分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def analyze_access_pattern():
"""分析访问模式"""
client_ip = get_client_ip()

# 检查访问频率
if is_high_frequency_access(client_ip):
print(f"检测到高频访问: {client_ip}")
return True

# 检查访问时间模式
if is_automated_access_pattern(client_ip):
print(f"检测到自动化访问模式: {client_ip}")
return True

# 检查请求头一致性
if is_inconsistent_headers(client_ip):
print(f"检测到不一致的请求头: {client_ip}")
return True

return False

def is_high_frequency_access(ip):
"""检查是否高频访问"""
# 这里可以实现基于Redis或数据库的频率限制
# 例如:每分钟超过60次请求
return False

def is_automated_access_pattern(ip):
"""检查是否自动化访问模式"""
# 检查请求间隔是否过于规律
# 检查是否缺少人类行为特征
return False

绕过技术详解

1. 前端绕过技术

浏览器环境伪装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 伪装浏览器环境
function disguiseBrowserEnvironment() {
// 移除webdriver标识
Object.defineProperty(navigator, 'webdriver', {
get: () => undefined
});

// 伪装插件
Object.defineProperty(navigator, 'plugins', {
get: () => [1, 2, 3, 4, 5] // 模拟5个插件
});

// 伪装语言
Object.defineProperty(navigator, 'languages', {
get: () => ['zh-CN', 'zh', 'en']
});

// 伪装屏幕分辨率
Object.defineProperty(screen, 'width', {
get: () => 1920
});
Object.defineProperty(screen, 'height', {
get: () => 1080
});
}

// 禁用WebRTC
function disableWebRTC() {
// 重写RTCPeerConnection
window.RTCPeerConnection = function() {
throw new Error('WebRTC is disabled');
};
}

Canvas指纹伪造

1
2
3
4
5
6
7
8
9
// 伪造Canvas指纹
function forgeCanvasFingerprint() {
const originalToDataURL = HTMLCanvasElement.prototype.toDataURL;

HTMLCanvasElement.prototype.toDataURL = function() {
// 返回正常的指纹数据
return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';
};
}

2. 后端绕过技术

请求头伪造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import requests

def forge_headers():
"""伪造请求头"""
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8',
'Accept-Encoding': 'gzip, deflate, br',
'Connection': 'keep-alive',
'Upgrade-Insecure-Requests': '1',
'Sec-Fetch-Dest': 'document',
'Sec-Fetch-Mode': 'navigate',
'Sec-Fetch-Site': 'none',
'Cache-Control': 'max-age=0'
}

# 移除代理相关头部
# 不设置X-Forwarded-For等头部

return headers

def rotate_user_agents():
"""轮换User-Agent"""
user_agents = [
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
]

import random
return random.choice(user_agents)

代理池技术

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import random
import time

class ProxyPool:
def __init__(self):
self.proxies = [
{'http': 'http://proxy1:port', 'https': 'https://proxy1:port'},
{'http': 'http://proxy2:port', 'https': 'https://proxy2:port'},
# 更多代理...
]
self.current_proxy = None

def get_random_proxy(self):
"""获取随机代理"""
return random.choice(self.proxies)

def rotate_proxy(self):
"""轮换代理"""
self.current_proxy = self.get_random_proxy()
return self.current_proxy

def make_request(self, url, **kwargs):
"""使用代理发送请求"""
proxy = self.rotate_proxy()

try:
response = requests.get(url, proxies=proxy, **kwargs)
return response
except Exception as e:
print(f"代理请求失败: {e}")
# 尝试下一个代理
return self.make_request(url, **kwargs)

# 使用示例
proxy_pool = ProxyPool()
response = proxy_pool.make_request('https://example.com', headers=forge_headers())

3. 进阶绕过技术

浏览器自动化绕过

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from selenium import webdriver
from selenium.webdriver.chrome.options import Options

def create_stealth_driver():
"""创建隐蔽的浏览器驱动"""
options = Options()

# 添加反检测选项
options.add_argument('--disable-blink-features=AutomationControlled')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)

# 设置用户代理
options.add_argument('--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')

driver = webdriver.Chrome(options=options)

# 执行反检测脚本
driver.execute_script("Object.defineProperty(navigator, 'webdriver', {get: () => undefined})")

return driver

分布式请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import asyncio
import aiohttp
import random

async def distributed_request(url, proxy_list):
"""分布式请求"""
tasks = []

for proxy in proxy_list:
task = make_async_request(url, proxy)
tasks.append(task)

# 并发执行请求
results = await asyncio.gather(*tasks, return_exceptions=True)

# 返回成功的结果
for result in results:
if not isinstance(result, Exception):
return result

return None

async def make_async_request(url, proxy):
"""异步请求"""
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, proxy=proxy) as response:
return await response.text()
except Exception as e:
raise e

检测与绕过的对抗演进

检测技术演进

第一代检测

  • 简单的User-Agent检测
  • 基本的IP黑名单
  • 简单的频率限制

第二代检测

  • 浏览器指纹识别
  • 行为模式分析
  • 地理位置验证

第三代检测

  • 机器学习检测
  • 实时行为分析
  • 多维度综合判断

绕过技术演进

第一代绕过

  • 简单的User-Agent伪造
  • 基础代理使用
  • 请求头修改

第二代绕过

  • 浏览器环境伪装
  • 代理池技术
  • 分布式请求

第三代绕过

  • 深度浏览器伪装
  • 智能代理选择
  • 行为模拟

结语

Web应用代理检测与绕过技术也是一个不断演进的攻防对抗过程,同样这篇文章总结也还是大体的冰山一角,实际应用场景中会有更多情况。

本文关键点

  • 多层检测:前端JavaScript检测与后端服务器检测机制简要分析
  • 技术演进:从简单检测到智能分析,从基础绕过到深度伪装,实际开发过程中的具体场景具体分析
持续输出技术分享,您的支持将鼓励我继续创作!