Piratage de l'API Google Translation

Google propose l' API Google Translation avec une structure de coûts basée sur l'utilisation dans le cadre de son Google Cloud. Il existe également une API non documentée qui peut être utilisée sans clé , mais qui refuse de fonctionner après seulement quelques requêtes. Lorsque vous utilisez la fonction de traduction de site Web de Google Chrome, il est à noter que les pages peuvent être traduites en très bonne qualité sans aucune limitation notable.


Apparemment, le modèle avancé de nmt est déjà utilisé ici. Mais quelle API Google Chrome utilise-t-il en interne pour traduire le contenu et cette API peut-elle également être adressée directement - même du côté serveur? Pour analyser le trafic réseau, des outils comme Wireshark ou Telerik Fiddler , qui peuvent également analyser le trafic chiffré, sont recommandés. Mais Chrome fournit même gratuitement les demandes qu'il envoie pour la traduction de la page: elles peuvent être facilement consultées à l'aide des Chrome DevTools:

Si vous effectuez une traduction, puis attrapez la requête POST cruciale à https://translate.googleapis.com via "Copier> Copier comme cURL (bash)" et exécutez-la dans un outil comme Postman , par exemple, vous pouvez renvoyer la requête sans aucun problème:

La signification des paramètres d'URL est également largement évidente:

CléExemple de valeurSens
anno3Mode d'annotation (affecte le format de retour)
clientte_libInformations sur le client (varie, la valeur est "webapp" via l'interface Web de Google Translate; a un effet sur le format de retour et la limitation du taux)
formathtmlFormat de chaîne (important pour la traduction des balises HTML)
v1.0Numéro de version de Google Translate
cléAIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgwClé API (voir ci-dessous)
logldvTE_20200210_00Version du protocole
sldeLangue originelle
tlfrLangue cible
spnmtModèle ML
tc1inconnue
sr1inconnue
tk709408.812158Jeton (voir ci-dessous)
Mode1inconnue

Certains en-têtes de requête sont également définis, mais ils peuvent généralement être ignorés. Après avoir désélectionné manuellement tous les en-têtes, y compris ceux de l' agent utilisateur , un problème d'encodage est découvert lors de la saisie de caractères spéciaux (ici lors de la traduction de " Hello World "):

Si vous réactivez l'agent utilisateur (qui ne fait généralement pas de mal), l'API délivre des caractères encodés en UTF-8:

Sommes-nous déjà là et avons-nous toutes les informations pour utiliser cette API en dehors de Google Chrome? Si vous modifiez la chaîne à traduire (champ de données q de la requête POST), par exemple, «Hello world» en «Hello world ! ", Nous recevons un message d'erreur:

Nous traduisons maintenant à nouveau celui-ci modifié dans Google Chrome en utilisant la fonction de traduction de site Web et constatons qu'en plus du paramètre q , le paramètre tk a également changé (tous les autres paramètres sont restés les mêmes):

Evidemment, c'est un token qui dépend de la chaîne dont la structure n'est pas facile à voir. Lorsque vous démarrez la traduction du site Web, les fichiers suivants sont chargés:

  • 1 fichier CSS: translateelement.css
  • 4 graphiques: translate_24dp.png (2x), gen204 (2x)
  • 2 fichiers JS: main_de.js , element_main.js

Les deux fichiers JavaScript sont obscurcis et minifiés. Des outils comme JS Nice et de4js nous aident désormais à rendre ces fichiers plus lisibles. Afin de les déboguer en direct, nous recommandons l'extension Chrome Requestly, qui tunnelise les fichiers distants localement à la volée:

Nous pouvons maintenant déboguer le code ( CORS doit d'abord être activé sur le serveur local). La section de code appropriée pour générer le jeton semble être cachée dans cette section du fichier element_main.js:

b7739bf50b2edcf636c43a8f8910def9

Ici, le texte est haché à l'aide de quelques décalages de bits . Mais malheureusement il nous manque encore une pièce du puzzle: en plus de l'argument a (qui est le texte à traduire), un autre argument b est passé à la fonction Bp () - une sorte de graine qui semble changer de temps en temps et qui inclut se transforme en hachage. Mais d'où vient-il? Si nous sautons à l'appel de fonction de Bp () , nous trouvons la section de code suivante:

b7739bf50b2edcf636c43a8f8910def9

La fonction Hq est déclarée au préalable comme suit:

b7739bf50b2edcf636c43a8f8910def9

Ici, le Deobfuscater a laissé des ordures; Après avoir remplacé String.fromCharCode ('...') par les chaînes de caractères respectives, supprimez le a () obsolète et reconstituez les appels de fonction [c (), c ()] , le résultat est:

b7739bf50b2edcf636c43a8f8910def9

Ou encore plus simple:

b7739bf50b2edcf636c43a8f8910def9

La fonction yq est précédemment définie comme:

b7739bf50b2edcf636c43a8f8910def9

La graine semble se trouver dans l'objet global google.translate._const._ctkk , qui est disponible au moment de l'exécution. Mais où est-il placé? Dans l'autre fichier JS main_de.js précédemment chargé , au moins il est également disponible au début. Nous ajoutons ce qui suit au début:

b7739bf50b2edcf636c43a8f8910def9

Dans la console, nous obtenons la graine actuelle:

Cela laisse Google Chrome lui-même, qui fournit apparemment la graine, comme dernière option. Heureusement, son code source (Chromium, y compris le composant Translate) est open source et donc accessible au public. Nous extrayons le référentiel localement et trouvons l'appel à la fonction TranslateScript :: GetTranslateScriptURL dans le fichier translate_script.cc dans le dossier components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

La variable avec l'URL est définie en dur dans le même fichier:

b7739bf50b2edcf636c43a8f8910def9

Si nous examinons maintenant le fichier element.js de plus près (après l'avoir défocalisé à nouveau), nous trouvons l'entrée fixe c._ctkk - l'objet google.translate est également défini en conséquence et le chargement de tous les éléments pertinents (que nous avons déjà découverts précédemment) est déclenché:

b7739bf50b2edcf636c43a8f8910def9

Maintenant, la clé de paramètre reste à prendre en considération (avec la valeur AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Cela semble être une clé d'API de navigateur générique (qui peut également être trouvée dans certains résultats Google ). Il est défini dans Chromium dans le fichier translate_url_util.cc dans le dossier components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

La clé est générée dans google_apis / google_api_keys.cc à partir d'une valeur factice:

b7739bf50b2edcf636c43a8f8910def9

Cependant, un test montre que les appels d'API fonctionnent de la même manière sans ce paramètre clé. Si vous testez l'API, vous récupérerez le code d'état 200 si vous réussissez. Si vous rencontrez ensuite une limite, vous récupérez le code d'état 411 avec le message "Les demandes POST nécessitent un en-tête de longueur de contenu ". Il est donc conseillé d' inclure cet en-tête (qui est automatiquement défini comme en- tête temporaire dans Postman).

Le format de retour des chaînes traduites est inhabituel lorsqu'il y a plusieurs phrases dans une même requête. Les phrases individuelles sont entourées des balises i- / b-HTML:

De plus, Google Chrome n'envoie pas l'intégralité du code HTML à l'API, mais enregistre les valeurs d'attribut telles que href dans la requête (et définit à la place des index afin que les balises puissent être attribuées ultérieurement côté client):

Si vous modifiez la valeur du client de clé POST de te_lib (Google Chrome) sur webapp ( site Web Google Traduction ), vous obtenez la chaîne finale traduite:

Le problème est que vous êtes beaucoup plus susceptible de vous heurter à une limitation de débit que via te_lib (à titre de comparaison: avec webapp, cela est atteint après 40 000 caractères, avec te_lib, il n'y a pas de limitation de débit). Nous devons donc examiner de plus près la façon dont Chrome analyse le résultat. Nous le trouverons ici dans element_main.js:

b7739bf50b2edcf636c43a8f8910def9

Si vous envoyez l'intégralité du code HTML à l'API, les attributs restent dans la réponse traduite. Nous n'avons donc pas à imiter l'intégralité du comportement d'analyse, mais uniquement à extraire la chaîne finale traduite de la réponse. Pour ce faire, nous construisons un petit analyseur de balises HTML qui supprime les balises <i> les plus externes, y compris leur contenu, et supprime les balises <b> les plus externes. Avec cette connaissance (après avoir installé les dépendances avec composer require fzaninotto / faker vielhuber / stringhelper ), nous pouvons maintenant créer une version côté serveur de l'API de traduction:

b7739bf50b2edcf636c43a8f8910def9

Voici les résultats d'un test initial réalisé sur cinq systèmes différents avec des bandes passantes et des adresses IP différentes:

PersonnageCaractères par demandeDuréeTaux d'erreurCoût via l'API officielle
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 €

Remarque: ce billet de blog comprenant tous les scripts a été rédigé à des fins de test uniquement. N'utilisez pas les scripts à des fins productives, mais utilisez plutôt l' API officielle de Google Traduction .

Retour