Desvendando uma Medida Anti-Bot Peculiar
Enquanto trabalhava na automação de buscas de carros para a DiscoverCars, esbarrei em uma medida de segurança fascinante — e um tanto incomum — projetada…

Enquanto trabalhava na automação de buscas de carros para a DiscoverCars, esbarrei em uma medida de segurança fascinante — e um tanto incomum — projetada para verificar usuários legítimos. Este post detalha meu processo de debug, como identifiquei o mecanismo-chave em jogo e por que esse método é ao mesmo tempo criativo e falho.
O Desafio Inicial
Para recuperar as listagens de carros, o sistema exigia um fluxo de sessão de inicialização antes de fazer uma requisição-chave:
curl 'https://www.discovercars.com/en/search/get-result-list/{session_id}?gToken={recaptcha_token}' \
-H 'accept: application/json, text/javascript, */*; q=0.01' \
-H 'user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36' \
-H 'x-requested-with: XMLHttpRequest'
Após várias tentativas, encontrei erros HTTP 400. Curiosamente, os tokens do reCAPTCHA v3 nem sequer eram validados. Isso me fez suspeitar que a verificação real estava acontecendo em outro lugar.
Encontrando o Verdadeiro Guardião
Comecei a analisar os cookies um por um e finalmente identifiquei a peça crítica: gtm_ga_ggbit. Diferente dos outros cookies, este era ativamente validado. No entanto, buscas online não retornaram quase nada sobre ele — exceto um post de um ano atrás no Freelancer, de um usuário chinês lutando com o mesmo problema.

Sem documentação disponível, mudei para uma abordagem debug-first para entender como esse cookie era definido.
Debug com Tampermonkey Salvando o Dia
Como eu suspeitava que o JavaScript era responsável por gerar o gtm_ga_ggbit, escrevi um script no Tampermonkey para dar hook em document.cookie e interromper a execução quando esse cookie específico fosse definido.
// ==UserScript==
// @name Cookie gtm_ga_ggbit Debugger
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Break into debugger when gtm_ga_ggbit cookie is set by any method.
// @author glizzykingdreko
// @match *://*/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function() {
'use strict';
const TARGET_NAME = 'gtm_ga_ggbit';
let lastCookieValue = document.cookie; // store initial cookies
// Helper: trigger debugger if target cookie present in document.cookie string
function triggerDebuggerIfCookieFound(source) {
// Check for the target cookie name in the cookie string
const cookieStr = document.cookie;
if (cookieStr && cookieStr.indexOf(TARGET_NAME + '=') !== -1) {
console.log(`[Tampermonkey] Cookie "${TARGET_NAME}" detected via ${source}.`);
alert(`Cookie ${TARGET_NAME} was set via ${source}!`);
debugger; // pause execution
}
}
// Periodic check (polling) as fallback for any missed events (e.g., Set-Cookie headers)
function startCookiePolling() {
setInterval(() => {
const current = document.cookie;
if (current !== lastCookieValue) { // cookies changed
if (current.indexOf(TARGET_NAME + '=') !== -1 && lastCookieValue.indexOf(TARGET_NAME + '=') === -1) {
// target cookie appeared (wasn't in last value)
triggerDebuggerIfCookieFound('polling');
}
lastCookieValue = current;
}
}, 500); // check every 500ms (adjust as needed)
}
// Hook document.cookie setter for JavaScript writes
const docCookieDesc = Object.getOwnPropertyDescriptor(Document.prototype, 'cookie');
if (docCookieDesc && docCookieDesc.configurable) {
const nativeSetter = docCookieDesc.set;
const nativeGetter = docCookieDesc.get;
Object.defineProperty(Document.prototype, 'cookie', {
configurable: true,
enumerable: true,
get() {
return nativeGetter.call(this);
},
set(value) {
if (typeof value === 'string' && value.indexOf(TARGET_NAME + '=') === 0) {
console.log(`[Tampermonkey] Cookie "${TARGET_NAME}" being set via document.cookie:`, value);
alert(`Cookie ${TARGET_NAME} is being set via script!`);
debugger;
}
// Actually set the cookie using native setter
nativeSetter.call(this, value);
}
});
}
// Hook Fetch API
if (window.fetch) {
const origFetch = window.fetch;
window.fetch = function(...args) {
return origFetch.apply(this, args).then(response => {
// After response, check cookies
triggerDebuggerIfCookieFound('fetch/XHR response');
return response;
});
};
}
// Hook XMLHttpRequest
if (window.XMLHttpRequest) {
const origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function(...args) {
// (Optional: store URL or method if needed for debugging)
this._debugUrl = args[1];
return origOpen.apply(this, args);
};
const origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function(...args) {
this.addEventListener('loadend', () => {
triggerDebuggerIfCookieFound('XHR response');
});
return origSend.apply(this, args);
};
}
// Hook WebSocket
if (window.WebSocket) {
const NativeWebSocket = WebSocket;
// Override WebSocket constructor
window.WebSocket = function(...args) {
const ws = new NativeWebSocket(...args);
ws.addEventListener('open', () => {
triggerDebuggerIfCookieFound('WebSocket handshake');
});
return ws;
};
// Copy prototype to maintain instanceof checks
window.WebSocket.prototype = NativeWebSocket.prototype;
window.WebSocket.CONNECTING = NativeWebSocket.CONNECTING;
window.WebSocket.OPEN = NativeWebSocket.OPEN;
window.WebSocket.CLOSING = NativeWebSocket.CLOSING;
window.WebSocket.CLOSED = NativeWebSocket.CLOSED;
}
// Use CookieStore API events if available (for modern browsers)
if (window.cookieStore && window.cookieStore.addEventListener) {
try {
window.cookieStore.addEventListener('change', event => {
// Check if our target cookie was added/changed
for (const change of event.changed) {
if (change.name === TARGET_NAME) {
console.log(`[Tampermonkey] Cookie "${TARGET_NAME}" change detected via CookieStore API.`);
alert(`Cookie ${TARGET_NAME} was set (CookieStore API)!`);
debugger;
}
}
});
} catch(e) {
console.warn('CookieStore API event registration failed:', e);
}
} else {
// Fallback to polling if CookieStore events not supported
startCookiePolling();
}
})();
Com esse script, consegui identificar exatamente onde o gtm_ga_ggbit estava sendo definido.

Descobrindo a Ofuscação Baseada em SVG
A grande revelação veio ao observar uma função JavaScript estranha que gerava o gtm_ga_ggbit usando atributos de cor de elementos SVG embutidos no frontend.

Lógica em JavaScript
var svgConvertor = [
[109, 97, 105, 110, 45, 119, 114, 97, 112, 112, 101, 114, 45, 98, 111, 100, 121],
[102, 97, 118, 105, 99, 111, 110, 95, 49, 54, 120, 49, 54],
[102, 97, 118, 105, 99, 111, 110, 95, 51, 50, 120, 51, 50],
[97, 112, 112, 108, 101, 95, 116, 111, 117, 99, 104, 95, 105, 99, 111, 110, 95, 49, 56, 48, 120, 49, 56, 48]
];
var svgContent = '';
for (let i in svgConvertor) {
let s = '';
for (let j in svgConvertor[i]) {
s += String.fromCharCode(svgConvertor[i][j]);
}
s = $('#' + s).attr('colour');
svgContent += s.split('').reverse().join('');
}
if (typeof myCookie !== 'undefined') {
myCookie.setCookie('gtm_ga_ggbit', svgContent);
}
Detalhando
- O array svgConvertor decifra para os IDs de quatro elementos do frontend: main-wrapper-body, favicon_16x16, favicon_32x32, apple_touch_icon_180x180
- Cada um desses elementos tem um atributo colour contendo uma string hex aparentemente aleatória.
- O script **extrai esses valores, os inverte, os concatena e os atribui a **gtm_ga_ggbit.
Exemplo:
<div class="main-wrapper" id="main-wrapper-body" colour="#d61805d3"></div>
<link id="favicon_16x16" colour="#8585e694">
<link id="favicon_32x32" colour="#7b589a35">
<link id="apple_touch_icon_180x180" colour="#5dae22af">
Essa abordagem é peculiar porque ofusca dinamicamente o valor do cookie com base em elementos renderizados no frontend, provavelmente tornando mais difícil para ferramentas automatizadas preverem o valor.
Contornando em Python
Apesar da criatividade, esse método é fácil de fazer engenharia reversa. Podemos simplesmente fazer o parse do frontend, extrair os valores necessários e reconstruir o cookie.
soup = BeautifulSoup(frontend_response.text, 'html.parser')
svg_content = ''
for element in [
"main-wrapper-body",
"favicon_16x16",
"favicon_32x32",
"apple_touch_icon_180x180"
]:
data = soup.find(id=element).get('colour')
svg_content += ''.join(reversed(data))
svg_content = svg_content.replace('#', '')
session.cookies.set("gtm_ga_ggbit", svg_content)
Considerações Finais
Essa foi uma das técnicas anti-bot mais criativas que já encontrei. Embora seja um jeito engenhoso de ofuscar a autenticação, no fim das contas ela é ineficaz porque:
- Os valores estão no lado do cliente e podem ser extraídos sem esforço.
- A lógica é previsível uma vez analisada.
- Adiciona complexidade sem melhorar significativamente a segurança.
Ainda assim, essa abordagem poderia enganar devs menos experientes e fazê-los perder tempo debugando outros fatores, tornando-a um estudo de caso divertido de segurança por obscuridade.
Adoraria saber se alguém mais já encontrou técnicas parecidas!
Me Siga e Entre em Contato
Se você gostou deste detalhamento, fique à vontade para me seguir em:
- GitHub: glizzykingdreko
- Medium: glizzykingdreko
Ou fale comigo diretamente:
- Discord: glizzykingdreko
- Email: [email protected]
Pule a engenharia reversa.
O Takion retorna cookies, headers e tokens novos para cada grande muro antibot. Um POST, sem navegador, primeira chamada em menos de uma hora.