Hackeo de API de traducción de Google

Como parte de su Google Cloud, Google ofrece la API de traducción de Google con una estructura de costos basada en el uso. También hay una API no documentada que se puede usar sin una clave , pero que se niega a funcionar después de unas pocas solicitudes. Cuando se utiliza la función de traducción de sitios web de Google Chrome, se nota que las páginas se pueden traducir con muy buena calidad sin limitaciones apreciables.


Aparentemente, el modelo nmt avanzado ya se está utilizando aquí. Pero, ¿qué API utiliza Google Chrome internamente para traducir el contenido? ¿Se puede abordar esta API también directamente, incluso en el lado del servidor? Para analizar el tráfico de la red, se recomiendan herramientas como Wireshark o Telerik Fiddler , que también pueden analizar el tráfico cifrado. Pero Chrome incluso entrega las solicitudes que envía para la traducción de la página de forma gratuita : son fácilmente accesibles a través de Chrome DevTools:

Si realiza una traducción, capture la solicitud POST crucial a https://translate.googleapis.com a través de "Copiar> Copiar como cURL (bash)" y ejecútela en una herramienta como Postman , por ejemplo, puede enviar la solicitud nuevamente sin ningún problema:

El significado de los parámetros de URL también es bastante obvio:

LlaveValor de ejemploSentido
anno3Modo de anotación (afecta el formato de devolución)
clientete_libInformación del cliente (varía, el valor es "webapp" a través de la interfaz web de Google Translate; tiene un efecto sobre el formato de devolución y la limitación de la tasa)
formatohtmlFormato de cadena (importante para traducir etiquetas HTML)
v1.0Número de versión del Traductor de Google
llaveAIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgwClave API (ver más abajo)
logldvTE_20200210_00Versión del protocolo
slDelawareLenguaje fuente
tlenLengua de llegada
spnmtModelo ML
tc1desconocido
sr1desconocido
tk709408.812158Token (ver más abajo)
Moda1desconocido

También se establecen algunos encabezados de solicitud, pero en su mayoría se pueden ignorar. Después de deseleccionar manualmente todos los encabezados, incluidos los del agente de usuario , se descubre un problema de codificación al ingresar caracteres especiales (aquí al traducir " Hola mundo "):

Si reactiva el agente de usuario (que generalmente no causa ningún daño), la API entrega caracteres codificados en UTF-8:

¿Ya estamos allí y tenemos toda la información para usar esta API fuera de Google Chrome? Si cambia la cadena a traducir (campo de datos q de la solicitud POST) de, por ejemplo, “Hola mundo” a “¡Hola mundo ! ", Recibimos un mensaje de error:

Ahora traducimos este modificado nuevamente dentro de Google Chrome usando la función de traducción del sitio web y encontramos que, además del parámetro q , el parámetro tk también ha cambiado (todos los demás parámetros han permanecido igual):

Al parecer, es un token que depende de la cadena, cuya estructura no es fácil de ver. Cuando inicia la traducción del sitio web, se cargan los siguientes archivos:

  • 1 archivo CSS: translateelement.css
  • 4 gráficos: translate_24dp.png (2x), gen204 (2x)
  • 2 archivos JS: main_de.js , element_main.js

Los dos archivos JavaScript están ofuscados y minimizados. Herramientas como JS Nice y de4js ahora nos ayudan a hacer que estos archivos sean más legibles. Para depurarlos en vivo, recomendamos la extensión Chrome Requestly, que canaliza archivos remotos localmente sobre la marcha:

Ahora podemos depurar el código (primero debe activarse CORS en el servidor local). La sección de código relevante para generar el token parece estar oculta en esta sección en el archivo element_main.js:

b7739bf50b2edcf636c43a8f8910def9

Aquí, el texto está codificado con la ayuda de algunos cambios de bits . Pero lamentablemente todavía nos falta una pieza del rompecabezas: además del argumento a (que es el texto a traducir), se pasa otro argumento b a la función Bp () - una especie de semilla que parece cambiar de vez en cuando y que también incluye fluye a hash. ¿Pero de dónde viene? Si saltamos a la llamada de función de Bp () , encontramos la siguiente sección de código:

b7739bf50b2edcf636c43a8f8910def9

La función Hq se declara de antemano de la siguiente manera:

b7739bf50b2edcf636c43a8f8910def9

Aquí el Deobfuscater dejó algunos desperdicios; Después de haber reemplazado String.fromCharCode ('...') con las respectivas cadenas de caracteres, elimine el obsoleto a () y junte las llamadas de función [c (), c ()] , el resultado es:

b7739bf50b2edcf636c43a8f8910def9

O incluso más fácil:

b7739bf50b2edcf636c43a8f8910def9

La función yq se define previamente como:

b7739bf50b2edcf636c43a8f8910def9

La semilla parece estar en el objeto global google.translate._const._ctkk , que está disponible en tiempo de ejecución. Pero, ¿dónde está ubicado? En el otro archivo JS main_de.js , previamente cargado , al menos también está disponible al principio. Agregamos lo siguiente al principio:

b7739bf50b2edcf636c43a8f8910def9

En la consola obtenemos la semilla actual:

Esto deja al propio Google Chrome, que aparentemente proporciona la semilla, como última opción. Afortunadamente, su código fuente (Chromium, incluido el componente Translate) es de código abierto y, por lo tanto, está disponible públicamente. Extraemos el repositorio localmente y encontramos la llamada a la función TranslateScript :: GetTranslateScriptURL en el archivo translate_script.cc en la carpeta components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

La variable con la URL está definida en el mismo archivo.:

b7739bf50b2edcf636c43a8f8910def9

Si ahora examinamos el archivo element.js más de cerca (después de desofuscar nuevamente), encontramos la entrada c._ctkk - el objeto google.translate también se configura en consecuencia y se activa la carga de todos los activos relevantes (que ya hemos descubierto anteriormente):

b7739bf50b2edcf636c43a8f8910def9

Ahora la clave del parámetro permanece en consideración (con el valor AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Parece ser una clave de API de navegador genérica (que también se puede encontrar en algunos resultados de Google ). Está configurado en Chromium en el archivo translate_url_util.cc en la carpeta componentes / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

La clave se genera en google_apis / google_api_keys.cc a partir de un valor ficticio:

b7739bf50b2edcf636c43a8f8910def9

Sin embargo, una prueba muestra que las llamadas a la API funcionan igual sin este parámetro clave. Si experimenta con la API, obtendrá el código de estado 200 si tiene éxito. Si luego se encuentra con un límite, obtiene el código de estado 411 con el mensaje "Las solicitudes POST requieren un encabezado de longitud de contenido ". Por lo tanto, es recomendable incluir este encabezado (que se configura automáticamente como un encabezado temporal en Postman).

El formato de retorno de las cadenas traducidas es inusual cuando hay varias oraciones en una solicitud. Las oraciones individuales están encerradas por las etiquetas i- / b-HTML:

Además, Google Chrome no envía todo el HTML a la API, pero guarda valores de atributos como href en la solicitud (y en su lugar establece índices para que las etiquetas se puedan asignar posteriormente en el lado del cliente):

Si cambia el valor del cliente clave POST de te_lib (Google Chrome) en webapp ( sitio web de traducción de Google ), obtiene la cadena traducida final:

El problema es que es mucho más probable que te encuentres con una limitación de velocidad que a través de te_lib (a modo de comparación: con webapp, esto se alcanza después de 40.000 caracteres, con te_lib no hay limitación de velocidad). Por lo tanto, debemos observar más de cerca cómo Chrome analiza el resultado. Lo encontraremos aquí en element_main.js:

b7739bf50b2edcf636c43a8f8910def9

Si envía el código HTML completo a la API, deja los atributos en la respuesta traducida. Por lo tanto, no tenemos que imitar todo el comportamiento de análisis, solo extraer la cadena final traducida de la respuesta. Para hacer esto, creamos un pequeño analizador de etiquetas HTML que descarta las etiquetas <i> más externas, incluido su contenido, y elimina las etiquetas <b> más externas. Con este conocimiento (después de instalar las dependencias con composer, se requiere fzaninotto / faker vielhuber / stringhelper ) ahora podemos construir una versión del lado del servidor de la API de traducción:

b7739bf50b2edcf636c43a8f8910def9

Los siguientes son los resultados de una prueba inicial que se llevó a cabo en cinco sistemas diferentes con diferentes anchos de banda y direcciones IP.:

PersonajeCaracteres por solicitudDuraciónTasa de errorCosto vía API oficial
13.064.662~25003: 36: 17h0%237,78€
24.530.510~25011: 09: 13h0%446,46€
49.060.211~25020: 39: 10h0%892,90€
99.074.487~100061: 24: 37h0%1803,16€
99.072.896~100062: 22: 20h0%1803,13€
Σ284.802.766~ Ø550Σ159: 11: 37h0%Σ 5183,41 €

Nota: Esta publicación de blog que incluye todos los scripts se escribió solo con fines de prueba. No utilice los guiones para uso productivo, en lugar de trabajar con el funcionario API Google Traductor .

Atrás