Files
Dotbypasser/extension/content/modules/ThreeDSecureHandlerModule.js
2026-01-10 17:24:58 +08:00

637 lines
18 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* ============================================================================
* 3D Secure (3DS) Handler Module (Modularized Version)
* ============================================================================
* 功能:
* 1. 攔截 Stripe 3DS 驗證相關的網絡請求
* 2. 監控驗證流程狀態 (進行中/成功/失敗)
* 3. 修改請求體以繞過指紋檢測
* 4. 通過 postMessage 和自定義事件廣播結果
* 5. 自動清理過期記錄防止內存泄漏
*
* 使用方式:
* const handler = new ThreeDSecureHandlerModule(config);
* handler.init();
* ============================================================================
*/
class ThreeDSecureHandlerModule {
constructor(config = {}) {
// 默認配置
this.config = {
enabled: true, // 是否啟用攔截
debug: false, // 調試模式
modifyPayload: false, // 是否修改請求體
cleanupInterval: 5000, // 清理間隔 (ms)
successTimeout: 30000, // 成功記錄過期時間 (ms)
maxCompletedRecords: 50, // 最大完成記錄數
targetDomains: [ // 目標域名
'stripe.com',
'stripejs.com'
],
targetKeywords: [ // URL 關鍵詞
'3d_secure',
'3ds',
'challenge',
'authenticate'
],
successIndicators: [ // 響應成功標識
'challenge_completed',
'3DS_COMPLETE',
'authenticated',
'success'
],
failureIndicators: [ // 響應失敗標識
'challenge_failed',
'3DS_FAILED',
'authentication_failed'
],
...config
};
// 運行時狀態
this.inProgressRequests = new Set(); // 進行中的請求
this.successfulRequests = new Map(); // 成功的請求 (url => timestamp)
this.completedRequests = new Set(); // 已完成的請求
this.cleanupIntervalId = null;
// 原始方法引用
this.originalFetch = null;
this.originalXHROpen = null;
this.originalXHRSend = null;
// 綁定方法上下文
this.handleFetch = this.handleFetch.bind(this);
this.cleanup = this.cleanup.bind(this);
}
/**
* 初始化模塊
*/
init() {
this.log('Initializing 3DS Handler Module...');
// 1. 暴露狀態到全局 (供外部檢查)
this.exposeGlobalState();
// 2. Hook 網絡請求
this.hookFetch();
this.hookXHR();
// 3. 啟動清理任務
this.startCleanupTask();
// 4. 註冊消息監聽
this.setupMessageListener();
this.log('Module initialized successfully');
}
/**
* 銷毀模塊
*/
destroy() {
// 恢復原始方法
if (this.originalFetch) {
window.fetch = this.originalFetch;
}
if (this.originalXHROpen) {
XMLHttpRequest.prototype.open = this.originalXHROpen;
}
if (this.originalXHRSend) {
XMLHttpRequest.prototype.send = this.originalXHRSend;
}
// 清理定時器
if (this.cleanupIntervalId) {
clearInterval(this.cleanupIntervalId);
}
this.log('Module destroyed');
}
/**
* 暴露狀態到全局
*/
exposeGlobalState() {
window.__3DSInProgress = this.inProgressRequests;
window.__3DSSuccessful = this.successfulRequests;
window.__3DSCompleted = this.completedRequests;
}
/**
* ========================================================================
* Fetch API Hook
* ========================================================================
*/
hookFetch() {
if (this.originalFetch) return; // 防止重複 hook
this.originalFetch = window.fetch;
const self = this;
window.fetch = async function(...args) {
return await self.handleFetch(this, args, self.originalFetch);
};
this.log('Fetch API hooked');
}
async handleFetch(context, args, originalFetch) {
const [url, options = {}] = args;
const urlString = this.normalizeUrl(url);
// 檢查是否為目標請求
if (!this.isTargetRequest(urlString)) {
return await originalFetch.apply(context, args);
}
this.log('Intercepted Fetch request:', urlString);
this.inProgressRequests.add(urlString);
// 修改請求體 (如果啟用)
if (this.config.modifyPayload && options.body) {
try {
options.body = this.modifyRequestBody(options.body, urlString);
this.log('Modified request body');
} catch (e) {
this.error('Failed to modify request body:', e);
}
}
// 執行原始請求
let response;
try {
response = await originalFetch.apply(context, [url, options]);
} catch (err) {
this.inProgressRequests.delete(urlString);
this.error('Fetch request failed:', err);
throw err;
}
// 處理響應
await this.processResponse(response, urlString);
return response;
}
/**
* ========================================================================
* XMLHttpRequest Hook
* ========================================================================
*/
hookXHR() {
if (this.originalXHROpen) return;
this.originalXHROpen = XMLHttpRequest.prototype.open;
this.originalXHRSend = XMLHttpRequest.prototype.send;
const self = this;
// Hook open 方法以獲取 URL
XMLHttpRequest.prototype.open = function(method, url, ...rest) {
this._3dsUrl = self.normalizeUrl(url);
this._3dsMethod = method;
return self.originalXHROpen.apply(this, [method, url, ...rest]);
};
// Hook send 方法以處理請求和響應
XMLHttpRequest.prototype.send = function(body) {
const url = this._3dsUrl;
if (!self.isTargetRequest(url)) {
return self.originalXHRSend.apply(this, [body]);
}
self.log('Intercepted XHR request:', url);
self.inProgressRequests.add(url);
// 修改請求體
if (self.config.modifyPayload && body) {
try {
body = self.modifyRequestBody(body, url);
self.log('Modified XHR body');
} catch (e) {
self.error('Failed to modify XHR body:', e);
}
}
// 監聽響應
const originalOnReadyStateChange = this.onreadystatechange;
this.onreadystatechange = function() {
if (this.readyState === 4) {
self.processXHRResponse(this, url);
}
if (originalOnReadyStateChange) {
originalOnReadyStateChange.apply(this, arguments);
}
};
return self.originalXHRSend.apply(this, [body]);
};
this.log('XHR hooked');
}
/**
* ========================================================================
* 請求/響應處理
* ========================================================================
*/
/**
* 標準化 URL
*/
normalizeUrl(url) {
try {
if (typeof url === 'string') return url;
if (url instanceof URL) return url.toString();
if (url && url.url) return url.url;
return String(url);
} catch (e) {
return '';
}
}
/**
* 判斷是否為目標請求
*/
isTargetRequest(url) {
if (!this.config.enabled || !url) return false;
// 檢查域名
const domainMatch = this.config.targetDomains.some(domain =>
url.includes(domain)
);
// 檢查關鍵詞
const keywordMatch = this.config.targetKeywords.some(keyword =>
url.toLowerCase().includes(keyword.toLowerCase())
);
return domainMatch || keywordMatch;
}
/**
* 修改請求體 (繞過指紋檢測)
*/
modifyRequestBody(body, url) {
// 這裡可以實現具體的修改邏輯
// 例如:移除瀏覽器指紋參數、修改 User-Agent 相關字段等
if (typeof body === 'string') {
try {
const json = JSON.parse(body);
// 移除常見的指紋字段
delete json.browser_fingerprint;
delete json.device_fingerprint;
delete json.canvas_fingerprint;
// 強制某些安全參數
if (json.challenge_type) {
json.challenge_type = 'frictionless'; // 嘗試跳過挑戰
}
return JSON.stringify(json);
} catch (e) {
// 不是 JSON可能是 FormData 或其他格式
return body;
}
}
return body;
}
/**
* 處理 Fetch 響應
*/
async processResponse(response, url) {
try {
const clone = response.clone();
const status = response.status;
if (status === 200) {
const text = await clone.text();
const verificationStatus = this.analyzeResponseText(text);
if (verificationStatus === 'success') {
this.handleSuccess(url, response.url);
} else if (verificationStatus === 'failed') {
this.handleFailure(url);
} else {
this.log('3DS request completed with unknown status');
}
}
} catch (e) {
this.error('Error processing response:', e);
} finally {
this.inProgressRequests.delete(url);
}
}
/**
* 處理 XHR 響應
*/
processXHRResponse(xhr, url) {
try {
if (xhr.status === 200) {
const text = xhr.responseText;
const verificationStatus = this.analyzeResponseText(text);
if (verificationStatus === 'success') {
this.handleSuccess(url, xhr.responseURL);
} else if (verificationStatus === 'failed') {
this.handleFailure(url);
}
}
} catch (e) {
this.error('Error processing XHR response:', e);
} finally {
this.inProgressRequests.delete(url);
}
}
/**
* 分析響應文本判斷驗證狀態
*/
analyzeResponseText(text) {
if (!text) return 'unknown';
const textLower = text.toLowerCase();
// 檢查成功標識
const isSuccess = this.config.successIndicators.some(indicator =>
textLower.includes(indicator.toLowerCase())
);
if (isSuccess) return 'success';
// 檢查失敗標識
const isFailure = this.config.failureIndicators.some(indicator =>
textLower.includes(indicator.toLowerCase())
);
if (isFailure) return 'failed';
return 'unknown';
}
/**
* 處理驗證成功
*/
handleSuccess(requestUrl, responseUrl) {
const timestamp = Date.now();
const key = `${requestUrl}_${timestamp}`;
this.successfulRequests.set(key, timestamp);
this.completedRequests.add(requestUrl);
this.log('✓ 3DS Verification Successful!', responseUrl);
// 廣播成功消息
this.broadcastEvent({
type: 'STRIPE_BYPASSER_EVENT',
eventType: '3DS_COMPLETE',
status: 'success',
url: responseUrl,
requestUrl: requestUrl,
timestamp: timestamp
});
// 觸發自定義 DOM 事件
this.dispatchDOMEvent('3DS_COMPLETE', {
status: 'success',
url: responseUrl,
requestUrl: requestUrl
});
}
/**
* 處理驗證失敗
*/
handleFailure(url) {
this.warn('✗ 3DS Verification Failed:', url);
this.broadcastEvent({
type: 'STRIPE_BYPASSER_EVENT',
eventType: '3DS_FAILED',
status: 'failed',
url: url,
timestamp: Date.now()
});
this.dispatchDOMEvent('3DS_FAILED', {
status: 'failed',
url: url
});
}
/**
* ========================================================================
* 事件廣播
* ========================================================================
*/
/**
* 通過 postMessage 廣播
*/
broadcastEvent(data) {
try {
window.postMessage(data, '*');
// 也嘗試向父窗口發送
if (window.parent !== window) {
window.parent.postMessage(data, '*');
}
} catch (e) {
this.error('Failed to broadcast event:', e);
}
}
/**
* 觸發自定義 DOM 事件
*/
dispatchDOMEvent(eventName, detail) {
try {
if (document) {
const event = new CustomEvent(eventName, { detail });
document.dispatchEvent(event);
}
} catch (e) {
this.error('Failed to dispatch DOM event:', e);
}
}
/**
* 監聽來自外部的消息
*/
setupMessageListener() {
window.addEventListener('message', (event) => {
if (event.data?.type === '3DS_HANDLER_UPDATE_CONFIG') {
this.updateConfig(event.data.config);
}
if (event.data?.type === '3DS_HANDLER_RESET') {
this.reset();
}
});
}
/**
* ========================================================================
* 清理與維護
* ========================================================================
*/
/**
* 啟動清理任務
*/
startCleanupTask() {
if (this.cleanupIntervalId) return;
this.cleanupIntervalId = setInterval(
this.cleanup,
this.config.cleanupInterval
);
this.log('Cleanup task started');
}
/**
* 清理過期記錄
*/
cleanup() {
const now = Date.now();
// 清理過期的成功記錄
for (const [key, timestamp] of this.successfulRequests.entries()) {
if (now - timestamp > this.config.successTimeout) {
this.successfulRequests.delete(key);
}
}
// 限制 completedRequests 大小
if (this.completedRequests.size > this.config.maxCompletedRecords) {
const entries = Array.from(this.completedRequests);
const toRemove = entries.slice(
0,
entries.length - this.config.maxCompletedRecords
);
toRemove.forEach(item => this.completedRequests.delete(item));
}
if (this.config.debug) {
this.log('Cleanup completed', {
successful: this.successfulRequests.size,
completed: this.completedRequests.size,
inProgress: this.inProgressRequests.size
});
}
}
/**
* 重置所有狀態
*/
reset() {
this.inProgressRequests.clear();
this.successfulRequests.clear();
this.completedRequests.clear();
this.log('Module state reset');
}
/**
* ========================================================================
* 配置管理
* ========================================================================
*/
/**
* 更新配置
*/
updateConfig(newConfig) {
Object.assign(this.config, newConfig);
this.log('Config updated:', this.config);
}
/**
* 獲取當前狀態
*/
getStatus() {
return {
inProgress: Array.from(this.inProgressRequests),
successful: Array.from(this.successfulRequests.keys()),
completed: Array.from(this.completedRequests),
config: this.config
};
}
/**
* ========================================================================
* 日誌工具
* ========================================================================
*/
log(...args) {
if (this.config.debug) {
console.log('[3DS Handler]', ...args);
}
}
warn(...args) {
if (this.config.debug) {
console.warn('[3DS Handler]', ...args);
}
}
error(...args) {
console.error('[3DS Handler]', ...args);
}
}
/**
* ============================================================================
* 全局 API 暴露
* ============================================================================
*/
window.ThreeDSecureHandlerModule = ThreeDSecureHandlerModule;
if (typeof THREE_DS_AUTO_INIT !== 'undefined' && THREE_DS_AUTO_INIT) {
const instance = new ThreeDSecureHandlerModule({
enabled: true,
debug: true,
modifyPayload: true
});
instance.init();
window.__3dsHandler = instance;
}
// ============================================================================
// Extension Compatibility Wrapper
// ============================================================================
window.addEventListener('message', (event) => {
if (event.source !== window) return;
const message = event.data;
if (message && message.type === 'INIT_MODULE' && message.moduleName === 'threeDSecure') {
if (window.__threeDSecureInstance) return;
try {
const instance = new ThreeDSecureHandlerModule(message.config);
instance.init();
window.__threeDSecureInstance = instance;
console.log('[Extension] 3D Secure Handler initialized');
} catch (error) {
console.error('[Extension] 3D Secure init failed:', error);
}
}
if (message && message.type === 'DESTROY_MODULE' && message.instanceKey === '__threeDSecureInstance') {
const instance = window.__threeDSecureInstance;
if (instance && typeof instance.destroy === 'function') {
instance.destroy();
delete window.__threeDSecureInstance;
}
}
});