davidteather / TikTok-Api

The Unofficial TikTok API Wrapper In Python
https://davidteather.github.io/TikTok-Api
MIT License
4.91k stars 984 forks source link

[BUG] - how to generate x-tt-params? #899

Open lazezo2 opened 2 years ago

lazezo2 commented 2 years ago

the api changed where now instead of sending the old params directly you have to encrypt them and send them encrypted as an x-tt-params header with sending a param with mstoken, xbogus, and the signature

https://github.com/davidteather/TikTok-Api/issues/695


import requests
headers = {
    'Host': 'm.tiktok.com',
    'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0',
    'Accept': '*/*',
    'Accept-Language': 'n-US,en;q=0.5',
    'Accept-Encoding': 'gzip, deflate, br',
    'Referer': 'https://www.tiktok.com/',
    'x-tt-params': '4mOJhGnMOdadxrLEy2bkmGSR2R38w8nZC8MQKREioTAU76aXIbW+KkRzj5O7qqbOI65rqSqkFAXNltiJ1p2YyvalYQ0VbxXkcXRyJfQSSRReg2K0v9Pg8C3JQ2XpgNJeK9TBQlzIhq658TurxN5EEGAUTrTmN1XStQ7Wb+vv8Rxg9TJYgVpSiPx6zpldv/vRCw8Z9HJzRGDHCnSz0A+qwk0sgzpoeEd8C1RAml95U1hXfWNKqTyrk6cvq6qPLZfK2ny3lb4XkHhFDEM7lrUIot9+cPFYsbeiECuPNWN8rcuarwOhB5yNv+uwAQiKKtvW1JtC0EbNLafHMsM0X2xEhkuFtCgo8BSAEI7HfRUY9nr/D8jhFGs/LFw5KIIJuQV5fkhAIF6BtqSe6P+6bSXDqfGaiSH87mE9CbnbnRtb6zfLPTSDu4X0SHPV1u9pPLCs0J5oB+hhY6QKl5Bh5vv9V3sOrcfLoPLUIguHDfDVrp9zXujFYlmyK5ALnr9XDf96n3wi/wV+z9hybUnWXkjXWOxcL/XDHaKHeTUFH6NGVeZnY7AdXCO9+q/R0IEdKIigw2/dKnE4fH77Afb8SxCD7OQRWGGJVLY+zQpSYbonxl8SsVjplpFuC7shMEbM1VJ6o9ppsYLMNGBS1as91aoRBSMLWsb/8G062SHW/boDzw5f4pbXANKb0Us7KAGH9v/eh+2vJhakpxW1c3kZkSKz4BgJm6q4Sse6u0ffSV/VFiIlo48GaFMVWeQgNNWcatnmORr25SfxqOl6A4Fx13NDb7uBE50EVvSh5AvZHV4D5zNtQ2HGSqYOOoH24e9f4I/IrwjxXeZpqVu1uJNK94fUCfUz7PajwiZmCwE8uGXGcTroO9S6JEZADRuz7N+0znGdzMfEmgF0MtCRLTY58IPmjuPwMkcEATrbNcx0IqaIZZHRkv6QI1hWE7StuWjgjBJiql1/qSAwcM7QJV1ZCY/vPcJ/6Wouhvi2Y68J5gKex2Feuwk6EDhJBxsSAz2x2XSh',
    'Origin': 'https://www.tiktok.com',
    'DNT': '1',
    'Connection': 'keep-alive',
    'Sec-Fetch-Dest': 'empty',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Site':  'same-site',
}
u="https://m.tiktok.com/api/post/item_list/?aid=1988&app_language=en&app_name=tiktok_web&browser_language=en-US&browser_name=Mozilla&browser_online=true&browser_platform=Win32&browser_version=5.0%20%28Windows%29&channel=tiktok_web&cookie_enabled=true&device_id=&device_platform=web_pc&focus_state=true&from_page=user&history_len=2&is_fullscreen=false&is_page_visible=true&os=windows&priority_region=&referer=&region=EG&screen_height=528&screen_width=939&tz_name=Etc%2FGMT-2&webcast_language=en&msToken=&X-Bogus=&_signature="

r1 = requests.get(u, headers=headers)
print(r1.text)

note that i removed device id and ms-token and X-Bogus and signature the api url is the same but changing x-tt-params will change the user id or the cursor! of the response data...

the big question is how to generate x-tt-params???

Altimis commented 2 years ago

I'm having the same question. Please if anyone knows how to solve this.

scotthaleen commented 2 years ago

I was able to reproduce the encrypted string using the pycryptodom library


from base64 import b64decode, b64encode
from urllib.parse import parse_qsl, urlencode

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

def encrypt(r):
    s = urlencode(r, doseq=True, quote_via=lambda s, *_: s)
    key = "webapp1.0+202106".encode("utf-8")
    cipher = AES.new(key, AES.MODE_CBC, key)
    ct_bytes = cipher.encrypt(pad(s.encode("utf-8"), AES.block_size))
    return b64encode(ct_bytes).decode("utf-8")

def decrypt(s):
    key = "webapp1.0+202106".encode("utf-8")
    cipher = AES.new(key, AES.MODE_CBC, key)
    ct = b64decode(s)
    s = unpad(cipher.decrypt(ct), AES.block_size)
    return dict(parse_qsl(s.decode("utf-8"), keep_blank_values=True))

You can test it by pulling the x-tt-param value from the request and running it through the decrypt to see the payload being sent.

Test:

from pprint import pprint
xttparams = "..." # value from request header 
pprint(decrypt(xttparams)) # see payload as python dict 
xttparams == encrypt(decrypt(xttparams)) # verify 

Usage:

payload = {
 'aid': '1988',
 'app_language': 'en',
 'app_name': 'tiktok_web',
 'battery_info': '1',
 'browser_language': 'en-US',
 'browser_name': 'Mozilla',
  ...
  }

xttparams = encrypt(payload) 
Programmer-yar commented 2 years ago

@scotthaleen Thanks! It is working great

Anhduchb01 commented 2 years ago

@scotthaleen after i decrypt xttparams , i have a param which is 'secUid': 'MS4wLjABAAAAcgUJiFIjRKsrjCW9DZEZrrG5Ord-p4jEX_YGCAXdWDAawwrZ0laVr2Blq4oZLEQR'. I think it is encrypted. Do you have solution for decrypting it ??

lazezo2 commented 2 years ago

Thanks to @scotthaleen ,it's working perfect now we can generate x-tt-params using @scotthaleen code without problem You are the man

inforserv commented 2 years ago

secUid

It's a secondary tiktok userID.

luispablo commented 2 years ago

Hey, @scotthaleen , where did the string / key "webapp1.0+202106" used to encrypt / decrypt come from?

I know it's in the webpage JS code but... how did you find it and / or know it was used for this?

(I need to know mostly to find it again whenever they decide to change it...)

Thanks!!!

scotthaleen commented 2 years ago

@luispablo it is hardcoded in the js for their encryption call. It is in the webapp-desktop.*.js

image

siqyka commented 2 years ago

Thanks to @scotthaleen ,it's working perfect now we can generate x-tt-params using @scotthaleen code without problem You are the man

hello,i use the code,but i failed,can u give me a example code?my email is ethan.71@163.com

siqyka commented 2 years ago

I was able to reproduce the encrypted string using the pycryptodom library

from base64 import b64decode, b64encode
from urllib.parse import parse_qsl, urlencode

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad

def encrypt(r):
    s = urlencode(r, doseq=True, quote_via=lambda s, *_: s)
    key = "webapp1.0+202106".encode("utf-8")
    cipher = AES.new(key, AES.MODE_CBC, key)
    ct_bytes = cipher.encrypt(pad(s.encode("utf-8"), AES.block_size))
    return b64encode(ct_bytes).decode("utf-8")

def decrypt(s):
    key = "webapp1.0+202106".encode("utf-8")
    cipher = AES.new(key, AES.MODE_CBC, key)
    ct = b64decode(s)
    s = unpad(cipher.decrypt(ct), AES.block_size)
    return dict(parse_qsl(s.decode("utf-8"), keep_blank_values=True))

You can test it by pulling the x-tt-param value from the request and running it through the decrypt to see the payload being sent.

Test:

from pprint import pprint
xttparams = "..." # value from request header 
pprint(decrypt(xttparams)) # see payload as python dict 
xttparams == encrypt(decrypt(xttparams)) # verify 

Usage:

payload = {
 'aid': '1988',
 'app_language': 'en',
 'app_name': 'tiktok_web',
 'battery_info': '1',
 'browser_language': 'en-US',
 'browser_name': 'Mozilla',
  ...
  }

xttparams = encrypt(payload) 

hello i use x-tt-params as a params then use decrypt() ,then i use the result as a params but the result of encrypt is different from x-tt-params,why? image

dylancaponi commented 2 years ago

@scotthaleen have you implemented this with TikTokAPI? Maybe you'd be willing to share your branch? 😀

andres-gv commented 2 years ago

Hi all, thanks for your help, based on your comments I worked on the implementation in R:

encode

decrypted_payload = "aid=1988&app_name=tiktok_web&channel=tiktok_web..."
passphrase <- charToRaw("webapp1.0+202106") 
key <- as.raw(passphrase)

x <- charToRaw(decrypted_payload)
y = openssl::aes_cbc_encrypt(x, key = key, iv = key)
encrypted_payload = openssl::base64_encode(y) 
encrypted_payload 

decode

encrypted_payload_tk = "4mOJhGnMOdadxrLEy2bkmGSR2R38w8...."
b64_decode = openssl::base64_decode(encrypted_payload_tk)
y_decode = openssl::aes_cbc_decrypt(b64_decode, key = key, iv = key)
x_decode = rawToChar(y_decode)
x_decode

Thanks

PATAPOsha commented 10 months ago

did someone find secret key "webapp1.0+202106" analog for Android app? in Android API there is x-tt-token param. It looks the same format, but I believe it has a different secret key.

louissiu198 commented 3 months ago

x-tt-token param in android api represents the account info, which I don't think you can generate it by someway, basically it does the same as sessionid