Google Translation API Hacking

Google oferuje Google Translation API ze strukturą kosztów opartą na użytkowaniu w ramach Google Cloud. Istnieje również nieudokumentowane API, którego można używać bez klucza , ale które odmawia działania po zaledwie kilku żądaniach. Korzystając z funkcji tłumaczenia stron internetowych w Google Chrome, można zauważyć, że strony mogą być tłumaczone w bardzo dobrej jakości bez zauważalnych ograniczeń.


Najwyraźniej zaawansowany model nmt jest już tutaj używany. Ale jakiego interfejsu API używa wewnętrznie przeglądarka Google Chrome do tłumaczenia treści i czy można uzyskać do niego bezpośredni dostęp – nawet po stronie serwera? Do analizy ruchu sieciowego polecamy narzędzia takie jak Wireshark lub Telerik Fiddler , które potrafią również analizować ruch zaszyfrowany. Ale Chrome nawet bezpłatnie wysyła żądania, które wysyła podczas tłumaczenia stron: można je łatwo przeglądać za pomocą narzędzi Chrome DevTools:

Jeśli wykonujesz tłumaczenie, następnie przechwyć kluczowe żądanie POST do https://translate.googleapis.com za pomocą „Kopiuj> Kopiuj jako cURL (bash)” i wykonaj je w narzędziu takim jak np. Postman , możesz bez problemu ponownie wysłać żądanie:

Znaczenie parametrów adresu URL jest również w większości oczywiste.:

KluczPrzykładowa wartośćZnaczenie
anno3Tryb adnotacji (wpływa na format zwrotu)
klientte_libInformacje o kliencie (zmienne; w interfejsie internetowym Google Tłumacz wartość to „webapp”; ma wpływ na format zwrotu i ograniczenie szybkości)
formathtmlFormat ciągu (ważny przy tłumaczeniu tagów HTML)
v1.0Numer wersji Tłumacza Google
kluczAIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgwKlucz API (patrz poniżej)
logldvTE_20200210_00Wersja protokołu
sldeJęzyk źródłowy
tlenJęzyk docelowy
spnmtModel ML
tc1nieznany
sr1nieznany
tk709408.812158Token (patrz poniżej)
Moda1nieznany

Niektóre nagłówki żądań również są ustawione - ale najczęściej można je zignorować. Po ręcznym odznaczeniu wszystkich nagłówków, w tym tych pochodzących z klienta użytkownika , podczas wprowadzania znaków specjalnych (tutaj podczas tłumaczenia „ Hello World ”) zostaje wykryty problem z kodowaniem:

Jeśli ponownie aktywujesz klienta użytkownika (to zazwyczaj nie powoduje żadnej szkody), API dostarcza zakodowane znaki UTF-8:

Czy osiągnęliśmy już nasz cel i mamy wszystkie informacje, aby korzystać z tego API poza Google Chrome? Jeśli jednak zmienisz ciąg do przetłumaczenia (pole danych q żądania POST) na przykład z „Hello World” na „Hello World ! ”, pojawi się komunikat o błędzie.:

Teraz wykonujemy kolejne tłumaczenie tego zmodyfikowanego ciągu znaków w przeglądarce Google Chrome, korzystając z funkcji tłumaczenia witryny, i odkrywamy, że oprócz parametru q , zmianie uległ także parametr tk (wszystkie pozostałe parametry pozostały takie same).:

Oczywiście jest to token zależny od łańcucha, którego strukturę nie jest łatwo dostrzec. Po uruchomieniu tłumaczenia strony ładowane są następujące pliki:

  • 1 plik CSS: translateelement.css
  • 4 grafiki: translate_24dp.png (2x), gen204 (2x)
  • 2 pliki JS: main_de.js, element_main.js

Te dwa pliki JavaScript są zaciemnione i zminimalizowane. Narzędzia takie jak JS Nice i de4js pomagają nam teraz uczynić te pliki bardziej czytelnymi. Aby zdebugować je na żywo, zalecamy rozszerzenie Chrome Requestly, które tuneluje zdalne pliki lokalnie w locie:

Teraz możemy debugować kod (najpierw CORS musi być aktywowany na serwerze lokalnym). Wydaje się, że odpowiednia sekcja kodu do generowania tokenu jest ukryta w pliku element_main.js w tej sekcji:

b7739bf50b2edcf636c43a8f8910def9

Tutaj tekst jest haszowany za pomocą kilku przesunięć . Ale niestety wciąż brakuje nam jednego elementu układanki: oprócz argumentu a (który jest tekstem do przetłumaczenia), do funkcji Bp () jest przekazywany kolejny argument b - rodzaj ziarna, które wydaje się zmieniać od czasu do czasu i obejmuje również wpada do haszowania. Ale skąd on pochodzi? Jeśli przejdziemy do wywołania funkcji Bp () , znajdziemy następującą sekcję kodu:

b7739bf50b2edcf636c43a8f8910def9

Funkcja Hq jest wcześniej zadeklarowana w następujący sposób:

b7739bf50b2edcf636c43a8f8910def9

Osoba usuwająca zaciemnienia zostawiła tutaj trochę śmieci; po zastąpieniu String.fromCharCode('...') odpowiednimi ciągami znaków, usunięciu przestarzałego a() i połączeniu wywołań funkcji [c(), c()] , otrzymamy:

b7739bf50b2edcf636c43a8f8910def9

Albo jeszcze łatwiej:

b7739bf50b2edcf636c43a8f8910def9

Funkcja yq została wcześniej zdefiniowana jako:

b7739bf50b2edcf636c43a8f8910def9

Zatem ziarno wydaje się znajdować w obiekcie globalnym google.translate._const._ctkk , który jest dostępny w czasie wykonywania. Ale gdzie jest umieszczony? Przynajmniej jest on dostępny także na początku w innym, wcześniej załadowanym pliku JS main_de.js . Na początku dodajemy co następuje:

b7739bf50b2edcf636c43a8f8910def9

W konsoli faktycznie otrzymujemy aktualne ziarno:

To pozostawia Google Chrome, który najwyraźniej zapewnia ziarno jako ostatnią opcję. Na szczęście jego kod źródłowy (Chromium, w tym komponent Tłumacz) jest open source i dlatego jest publicznie dostępny. Pobieramy repozytorium lokalnie i znajdujemy wywołanie funkcji TranslateScript :: GetTranslateScriptURL w pliku translate_script.cc w folderze Components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

Zmienna z adresem URL jest trwale zdefiniowana w tym samym pliku:

b7739bf50b2edcf636c43a8f8910def9

Jeśli teraz bliżej przyjrzymy się plikowi element.js (po dalszym rozjaśnieniu), znajdziemy twardy wpis c._ctkk - obiekt google.translate również zostanie odpowiednio ustawiony i uruchomione zostanie ładowanie wszystkich odpowiednich zasobów (które już wcześniej odkryliśmy):

b7739bf50b2edcf636c43a8f8910def9

Teraz do rozważenia pozostaje klucz parametru (z wartością AIzaSyBOti4mM-6x9WDnZIjIeyEU21OpBXqWBgw). Wygląda na to, że jest to ogólny klucz API przeglądarki (który można również znaleźć w niektórych wynikach Google ). Jest ustawiony w Chromium w pliku translate_url_util.cc w folderze Components / translate / core / browser:

b7739bf50b2edcf636c43a8f8910def9

Klucz jest generowany w google_apis / google_api_keys.cc z fikcyjnej wartości:

b7739bf50b2edcf636c43a8f8910def9

Jednak testy pokazują, że wywołania API działają równie dobrze bez tego kluczowego parametru. Eksperymentowanie z API zwraca teraz kod statusu 200 w przypadku pomyślnych wywołań. Po osiągnięciu limitu zwracany jest kod statusu 411 z komunikatem „ Żądania POST wymagają nagłówka o długości zawartości ”. Dlatego zaleca się również dołączenie tego nagłówka (który jest automatycznie ustawiany jako nagłówek tymczasowy w programie Postman).

Format zwracanych przetłumaczonych ciągów jest nietypowy, gdy w jednym żądaniu jest kilka zdań. Poszczególne zdania są otoczone znacznikami i- / b-HTML:

Ponadto Google Chrome nie wysyła pełnego kodu HTML do interfejsu API, ale zapisuje w żądaniu wartości atrybutów, takie jak href (zamiast tego ustawia indeksy, aby tagi można było później przypisać po stronie klienta):

Jeśli zmienisz wartość klienta klucza POST z te_lib (Google Chrome) na webapp ( strony pomocną ), można uzyskać ostateczną tłumaczone ciąg:

Problem polega na tym, że istnieje większe prawdopodobieństwo, że napotkasz ograniczenie szybkości niż przez te_lib (dla porównania: w przypadku aplikacji webowej jest to osiągane po 40 000 znaków, w przypadku te_lib nie ma ograniczenia szybkości). Musimy więc przyjrzeć się bliżej, jak Chrome analizuje wynik. Znajdziemy go tutaj w element_main.js:

b7739bf50b2edcf636c43a8f8910def9

Jeśli wyślesz cały kod HTML do interfejsu API, atrybuty zostaną pozostawione w przetłumaczonej odpowiedzi. Dlatego nie musimy imitować całego zachowania parsowania, a jedynie wyodrębnić końcowy, przetłumaczony ciąg z odpowiedzi. Aby to zrobić, tworzymy mały parser tagów HTML, który odrzuca najbardziej zewnętrzne tagi <i> wraz z ich zawartością i usuwa najbardziej zewnętrzne tagi <b>. Dzięki tej wiedzy (po zainstalowaniu zależności z kompozytorem wymagają fzaninotto / faker vielhuber / stringhelper ) możemy teraz zbudować wersję API tłumaczenia po stronie serwera:

b7739bf50b2edcf636c43a8f8910def9

Poniżej znajdują się wyniki wstępnego testu, który został przeprowadzony na pięciu różnych systemach o różnych przepustowościach i adresach IP:

PostaćZnaki na żądanieTrwanieWskaźnik błędówKoszt za pośrednictwem oficjalnego interfejsu API
13.064.662~25003:36:170%237,78 €
24.530.510~25011:09:130%446,46 €
49.060.211~25020:39:100%892,90 €
99.074.487~100061:24:370%1803,16 €
99.072.896~100062:22:200%1803,13 €
Σ284,802,766~ Ø550Σ 159: 11: 37h0%5183,41 €

Uwaga: ten wpis na blogu zawierający wszystkie skrypty został napisany wyłącznie do celów testowych. Nie stosować skrypty do użytku produkcyjnego, zamiast pracować z oficjalnego API pomocna .

Plecy