Add support for encrypted query parameters

This commit is contained in:
Fijxu 2025-07-06 20:27:10 -04:00
parent df8839d1f0
commit f5aa881324
No known key found for this signature in database
GPG Key ID: 32C1DDF333EDA6A4
5 changed files with 64 additions and 4 deletions

View File

@ -568,6 +568,21 @@ hmac_key: "CHANGE_ME!!"
##
#playlist_length_limit: 500
##
## Encrypts query params 'ip' and 'pot'
## This is useful if you don't want to leak the IP address
## and PoToken used when fetching a Youtube video.
##
## Note: This will only work if the 'Proxy videos' preference is enabled
## on the Player preferences. Users that do not have 'Proxy videos' enabled
## will be able to see the IP address and PoToken used on videoplayback
## requests.
##
## Accepted values: true, false
## Default: false
##
#encrypt_query_params: false
#########################################
#
# Default user preferences

View File

@ -183,6 +183,8 @@ class Config
# Playlist length limit
property playlist_length_limit : Int32 = 500
property encrypt_query_params : Bool = false
def disabled?(option)
case disabled = CONFIG.disable_proxy
when Bool

View File

@ -1,3 +1,5 @@
require "uri/params/serializable"
# See http://www.evanmiller.org/how-not-to-sort-by-average-rating.html
def ci_lower_bound(pos, n)
if n == 0
@ -384,12 +386,17 @@ def parse_link_endpoint(endpoint : JSON::Any, text : String, video_id : String)
return text
end
def encrypt_ecb_without_salt(data, key)
def ecb_without_salt(data : String, key : String, mode : Symbol)
cipher = OpenSSL::Cipher.new("aes-128-ecb")
cipher.encrypt
case mode
when :encrypt
cipher.encrypt
when :decrypt
cipher.decrypt
end
cipher.key = key
io = IO::Memory.new
io = IO::Memory.new(data.bytesize + 16)
io.write(cipher.update(data))
io.write(cipher.final)
io.rewind
@ -399,6 +406,27 @@ end
def invidious_companion_encrypt(data)
timestamp = Time.utc.to_unix
encrypted_data = encrypt_ecb_without_salt("#{timestamp}|#{data}", CONFIG.invidious_companion_key)
encrypted_data = ecb_without_salt("#{timestamp}|#{data}", CONFIG.invidious_companion_key, :encrypt)
return Base64.urlsafe_encode(encrypted_data)
end
struct PrivateParams
include URI::Params::Serializable
include JSON::Serializable
property ip : String = ""
property pot : String = ""
end
def encrypt_query_params(query_params : URI::Params) : String
private_params = PrivateParams.from_www_form(query_params.to_s).to_json
encrypted_data = ecb_without_salt(private_params, CONFIG.hmac_key, :encrypt)
return Base64.urlsafe_encode(encrypted_data)
end
def decrypt_query_params(query_param_data : String) : PrivateParams
query_param_data = Base64.decode_string(query_param_data)
decrypted_data = ecb_without_salt(query_param_data, CONFIG.hmac_key, :decrypt)
private_params = PrivateParams.from_json(decrypted_data)
return private_params
end

View File

@ -9,6 +9,13 @@ module Invidious::HttpServer
# Add some URL parameters
params = url.query_params
if CONFIG.encrypt_query_params
encrypted_data = encrypt_query_params(params)
params["enc"] = "true"
params["data"] = encrypted_data
params.delete("ip")
params.delete("pot")
end
params["host"] = url.host.not_nil! # Should never be nil, in theory
params["region"] = region if !region.nil?
url.query_params = params

View File

@ -4,6 +4,14 @@ module Invidious::Routes::VideoPlayback
locale = env.get("preferences").as(Preferences).locale
query_params = env.params.query
if query_params["enc"]? == "true"
decrypted_data = decrypt_query_params(query_params["data"])
query_params["ip"] = decrypted_data.ip
query_params["pot"] = decrypted_data.pot
query_params.delete("enc")
query_params.delete("data")
end
fvip = query_params["fvip"]? || "3"
mns = query_params["mn"]?.try &.split(",")
mns ||= [] of String