@@ -627,15 +627,50 @@ def sign_request(params, options):
627627 return params
628628
629629
630- def api_sign_request (params_to_sign , api_secret , algorithm = SIGNATURE_SHA1 ):
631- params = [_encode_param (k + "=" + (
632- "," .join (v ) if isinstance (v , list ) else
633- str (v ).lower () if isinstance (v , bool ) else
634- str (v )
635- )) for k , v in params_to_sign .items () if v ]
636- to_sign = "&" .join (sorted (params ))
630+ def api_sign_request (params_to_sign , api_secret , algorithm = SIGNATURE_SHA1 , signature_version = 2 ):
631+ """
632+ Signs API request parameters using the specified algorithm and signature version.
633+
634+ :param params_to_sign: Parameters to include in the signature
635+ :param api_secret: API secret key
636+ :param algorithm: Signature algorithm (default: SHA1)
637+ :param signature_version: Signature version (default: 2)
638+ - Version 1: Original behavior without parameter encoding
639+ - Version 2+: Includes parameter encoding to prevent parameter smuggling
640+ :return: Computed signature
641+ """
642+ to_sign = api_string_to_sign (params_to_sign , signature_version )
637643 return compute_hex_hash (to_sign + api_secret , algorithm )
638644
645+
646+ def api_string_to_sign (params_to_sign , signature_version = 2 ):
647+ """
648+ Generates a string to be signed for API requests.
649+
650+ :param params_to_sign: Parameters to include in the signature
651+ :param signature_version: Version of signature algorithm to use:
652+ - Version 1: Original behavior without parameter encoding
653+ - Version 2+ (default): Includes parameter encoding to prevent parameter smuggling
654+ :return: String to be signed
655+ """
656+ params = []
657+ for k , v in params_to_sign .items ():
658+ if v :
659+ if isinstance (v , list ):
660+ value = "," .join (v )
661+ elif isinstance (v , bool ):
662+ value = str (v ).lower ()
663+ else :
664+ value = str (v )
665+
666+ param_string = k + "=" + value
667+ if signature_version >= 2 :
668+ param_string = _encode_param (param_string )
669+ params .append (param_string )
670+
671+ return "&" .join (sorted (params ))
672+
673+
639674def _encode_param (value ):
640675 """
641676 Encodes a parameter for safe inclusion in URL query strings.
@@ -648,6 +683,7 @@ def _encode_param(value):
648683 """
649684 return str (value ).replace ("&" , "%26" )
650685
686+
651687def breakpoint_settings_mapper (breakpoint_settings ):
652688 breakpoint_settings = copy .deepcopy (breakpoint_settings )
653689 transformation = breakpoint_settings .get ("transformation" )
@@ -1587,10 +1623,12 @@ def verify_api_response_signature(public_id, version, signature, algorithm=None)
15871623 parameters_to_sign = {'public_id' : public_id ,
15881624 'version' : version }
15891625
1626+ # Use signature version 1 for backward compatibility
15901627 return signature == api_sign_request (
15911628 parameters_to_sign ,
15921629 cloudinary .config ().api_secret ,
1593- algorithm or cloudinary .config ().signature_algorithm
1630+ algorithm or cloudinary .config ().signature_algorithm ,
1631+ signature_version = 1
15941632 )
15951633
15961634
0 commit comments