imagekit-developer / imagekit-php

PHP SDK for ImageKit.io API.
https://imagekit.io
MIT License
42 stars 14 forks source link

Invalid signature when signing a URL containing a diacritic #59

Closed nbolender closed 6 months ago

nbolender commented 1 year ago

I am having an issue with signed URLs. When the URL contains a diacritic (“é”), I get an error indicating an invalid request signature. I am not sure if the issue lies with the PHP SDK or with the server.

An example URL that is failing is: https://ik.imagekit.io/patchboard/echl/tr:c-at_max,fo-auto,ar-16-9,w-1000/production/tr/post/679/featured_image/7a32dac7-94d3-4617-b093-bb9c403c1e68/Pascal-Rhéaume-1920-1080.jpeg?ik-s=ea10ab0f3dd47f19921f9851576805edcaadfb71

harshit-budhraja commented 11 months ago

The error looks like a 401 (Unauthorised), which is thrown when the request signature passed as a query param ik-s is invalid.

@nbolender Is this happening with any other URLs on the same account identifier i.e. patchboard/echl? Probably one without a diacritic (“é”).

nbolender commented 11 months ago

@harshit-budhraja This does not occur on URLs without a diacritic (using the same SDK and same credentials). I believe it is an error in generating the signature, either in this SDK or on the server.

harshit-budhraja commented 11 months ago

@nbolender As per the documentation:

Signing the URLs adds additional query parameters to ensure that image transformations cannot be altered from the URL. If someone tries to modify the image transformation or the image URL or use it beyond its intended expiry time, a 401 Unauthorised response status code is returned.

For the image in question, can we confirm the following:

  1. The signature is created using the SDK and not via your own code? If latter is the case, we'd want to make sure that the signing logic is correct. Ref: https://docs.imagekit.io/features/security/signed-urls#pseudo-code-for-signed-url-generation

  2. Is the URL you're trying to access exactly same (including the transformation params) as the the one used to generate the signature? In this case, the path which should ideally be signed is: echl/production/tr/post/679/featured_image/7a32dac7-94d3-4617-b093-bb9c403c1e68/Pascal-Rhéaume-1920-1080.jpeg and the transformation parameters as required.

  3. The expiry is set correctly? Trying to access an expired image also gives 401.

harshit-budhraja commented 11 months ago

@nbolender I've tried reproducing the mentioned issue but it seems that it's working perfectly fine for me with diacritics.

Versions:

"name": "imagekit/imagekit",
"version": "3.0.3",
➜  php-test composer -V
Composer version 2.6.5 2023-10-06 10:11:52
➜  php-test php -v     
PHP 8.2.12 (cli) (built: Oct 24 2023 19:22:16) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.12, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.12, Copyright (c), by Zend Technologies

Here's my sample code:

<?php

// SDK initialization

// Require the Composer autoloader.
require 'vendor/autoload.php';
use ImageKit\ImageKit;  

$imageKit = new ImageKit(
    "public key",
    "private key",
    "url"
);

$imageURL = $imageKit->url([
    "path" => "/four-penguins-with-é.png",
    "queryParameters" => 
    [
        "v" => "123"
    ],
    "transformation" => [
        [
            "e-shadow" => "st-50_bl-15"
        ]
    ],
    "signed" => true,
    "expireSeconds" => 86400,
]);

echo $imageURL;

?>

And the generated signed url works for me: https://ik.imagekit.io/oq1pyotaq/tr:e-shadow-st-50_bl-15/four-penguins-with-é.png?v=123&ik-t=1699616879&ik-s=0ea0d21e4a874e519a4a2f416e540561fe978866%25

image

imagekitio commented 11 months ago

@harshit-budhraja try changing signature in your URL, if it still works that means correctness of signature is not being tested. You would need to ensure Allow only signed URL setting is turned on for signature check to matter.

nbolender commented 11 months ago

@harshit-budhraja Yes, I am using this PHP SDK v3.0.3. I'm not using expire dates, but using it does not fix the issue.

Note that an important difference between my example and yours is that you are using your account's default URL endpoint, and I am using a custom one.

php > $imagekit = new \ImageKit\ImageKit($public, $private, 'https://ik.imagekit.io/patchboard/echl');

php > echo $imagekit->url(['path' => '/four-penguins-with-e.png', 'signed' => true]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-e.png?ik-s=1754cb577de25067b61728d415ce74be56e054f3
# Works

php > echo $imagekit->url(['path' => '/four-penguins-with-é.png', 'signed' => true]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-é.png?ik-s=b670c33b4d9fcfe6e1ef198b7df6ed4ae85c9e47
# Doesn't work

php > echo $imagekit->url(['path' => '/four-penguins-with-e.png', 'signed' => true, 'expireSeconds' => 604800]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-e.png?ik-t=1700154382&ik-s=f598511ccc5008d0645de4c1daaa63226c5b1c5c
# Works

php > echo $imagekit->url(['path' => '/four-penguins-with-é.png', 'signed' => true, 'expireSeconds' => 604800]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-é.png?ik-t=1700154389&ik-s=325533548b97efbba592549d2b2af01f8ef32f53
# Doesn't work

Adding a transformation doesn't change anything:

php > echo $imagekit->url(['path' => '/four-penguins-with-e.png', 'signed' => true, 'transformation' => [['e-shadow' => 'st-50_bl-15']]]);
https://ik.imagekit.io/patchboard/echl/tr:e-shadow-st-50_bl-15/four-penguins-with-e.png?ik-s=fb3010cd3b120d6990f271968ea10460ad502818
# Works

php > echo $imagekit->url(['path' => '/four-penguins-with-é.png', 'signed' => true, 'transformation' => [['e-shadow' => 'st-50_bl-15']]]);
https://ik.imagekit.io/patchboard/echl/tr:e-shadow-st-50_bl-15/four-penguins-with-é.png?ik-s=d6731d835653345c6c1d578d9ec39b9bb449c107
# Doesn't work

I even tried moving the URL endpoint to the path, but that does not work for any image:

php > $imagekit = new \ImageKit\ImageKit($public, $private, 'https://ik.imagekit.io');

php > echo $imagekit->url(['path' => '/patchboard/echl/four-penguins-with-e.png', 'signed' => true]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-e.png?ik-s=00c996b5dc353ca2fd27941acb54d749db768a2b
# Doesn't work

php > echo $imagekit->url(['path' => '/patchboard/echl/four-penguins-with-é.png', 'signed' => true]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-é.png?ik-s=ca11d66a0a3710e7743b44b7af70c22f5a972eb8
# Doesn't work

php > $imagekit = new \ImageKit\ImageKit($public, $private, 'https://ik.imagekit.io/patchboard');

php > echo $imagekit->url(['path' => '/echl/four-penguins-with-e.png', 'signed' => true]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-e.png?ik-s=a5531aa7948c4de65c995b7c220d5b639102e2ce
# Doesn't work

php > echo $imagekit->url(['path' => '/echl/four-penguins-with-é.png', 'signed' => true]);
https://ik.imagekit.io/patchboard/echl/four-penguins-with-é.png?ik-s=aa20b72737683f7466d1c642f0cea8d983a44053
# Doesn't work
nbolender commented 11 months ago

And to be clear I am using an S3 origin.

I tried uploading the files to the ImageKit media library to test, but even the upload fails in the dashboard when uploading a file with a diacritic.

Screenshot 2023-11-09 at 12 23 07 PM
harshit-budhraja commented 10 months ago

@nbolender We've successfully investigated this. Here're the findings:

Most browser engines encode special characters, diacritics or characters from different charsets to UTF-8. For example, (diacritic) is encoded as e%CC%81, and so on. What this means is - that while encoding /four-penguins-with-é.png, the signing happens on the URL with the diacritic é, but while accessing the URL via a browser or via a mobile platform SDK, the URL being accessed is the UTF-8 encoded one. Hence, to fix this - Use a UTF-8 encoded URL at the time of signing.

For more details: https://docs.imagekit.io/features/security/signed-urls#signed-urls-with-special-characters-and-diacritics

nbolender commented 10 months ago

@harshit-budhraja Well, what you are doing is not encoding the URL in UTF-8, but instead taking a UTF-8 string and making it ASCII. I would argue that this is the responsibility of the SDK if it is a requirement.

The example given also does not work, because it will encode the forward slash as %2F and fail. To get it to work, I have to do something like this:

$path = '/penguin/four-penguins-with-é.png';
$parts = explode('/', $path);

$new_path = '';
foreach ($parts as $part) {
  if (!empty($part)) {
    $new_path .= '/' . urlencode($part);
  }
}

That's a lot of work, considering I had a properly-encoded UTF-8 string to start with, that works when accessing the source image directly from S3.

imagekitio commented 10 months ago

@nbolender it makes sense to handle this in SDK. We will review it again and keep this issue open for now. Thank you so much for providing this information.

imagekitio commented 6 months ago

@nbolender this is fixed in version 4.0.1. PR - https://github.com/imagekit-developer/imagekit-php/pull/65