Кратчайшая возможная кодированная строка с возможностью декодирования (сокращать URL) с использованием только PHP

Я ищу метод, который кодирует строку до кратчайшей возможной длины и позволяет ее декодировать (чистый PHP, без SQL). У меня есть рабочий скрипт, но я не удовлетворен длиной закодированной строки.

СЦЕНАРИЙ:

Ссылка на изображение (зависит от разрешения файла, которое я хочу показать пользователю):

  • www.mysite.com/share/index.php?img=/dir/dir/hi-res-img.jpg&w=700&h=500

Закодированная ссылка (поэтому пользователь не может угадать, как получить большее изображение):

  • www.mysite.com/share/encodedQUERYstring

Итак, в основном, я хотел бы кодировать только часть поискового запроса URL:

  • IMG = / DIR / DIR / Hi-Res-IMG.jpg & W = 700 & ч = 500

Метод, который я использую прямо сейчас, будет кодировать указанную выше строку запроса:

  • y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA

Метод, который я использую:

$raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500'; $encoded_query_string = base64_encode(gzdeflate($raw_query_string)); $decoded_query_string = gzinflate(base64_decode($encoded_query_string)); 

Как сократить закодированный результат и все еще иметь возможность расшифровать его, используя только PHP?

    Я подозреваю, что вам нужно будет больше думать о методе хэширования, если вы не хотите, чтобы он был декодирован пользователем. Проблема с base64 заключается в том, что строка base64 выглядит как строка base64. Есть хороший шанс, что кто-то, достаточно опытный, чтобы смотреть на ваш источник страницы, вероятно, тоже это узнает.

    Первая часть:

    метод, который кодирует строку до кратчайшей возможной длины

    Если вы будете гибки в своих словах / символах URL, это будет хорошим стартовым местом. Поскольку gzip делает большую часть своих выигрышей, используя обратные ссылки, мало смысла, поскольку строка настолько коротка.

    Рассмотрим ваш пример – вы сохранили только 2 байта в сжатии, которые снова потеряны в дополнении base64:

    Non-gzipped: string(52) "aW1nPS9kaXIvZGlyL2hpLXJlcy1pbWcuanBnJnc9NzAwJmg9NTAw"

    Gzipped: string(52) "y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA=="

    Если вы уменьшите размер своего словаря, это, естественно, позволит вам улучшить сжатие. Допустим, мы удаляем некоторую избыточную информацию

    Взгляните на функции:

     function compress($input, $ascii_offset = 38){ $input = strtoupper($input); $output = ''; //We can try for a 4:3 (8:6) compression (roughly), 24 bits for 4 chars foreach(str_split($input, 4) as $chunk) { $chunk = str_pad($chunk, 4, '='); $int_24 = 0; for($i=0; $i<4; $i++){ //Shift the output to the left 6 bits $int_24 <<= 6; //Add the next 6 bits //Discard the leading ascii chars, ie make $int_24 |= (ord($chunk[$i]) - $ascii_offset) & 0b111111; } //Here we take the 4 sets of 6 apart in 3 sets of 8 for($i=0; $i<3; $i++) { $output = pack('C', $int_24) . $output; $int_24 >>= 8; } } return $output; } 

    А также

     function decompress($input, $ascii_offset = 38) { $output = ''; foreach(str_split($input, 3) as $chunk) { //Reassemble the 24 bit ints from 3 bytes $int_24 = 0; foreach(unpack('C*', $chunk) as $char) { $int_24 <<= 8; $int_24 |= $char & 0b11111111; } //Expand the 24 bits to 4 sets of 6, and take their character values for($i = 0; $i < 4; $i++) { $output = chr($ascii_offset + ($int_24 & 0b111111)) . $output; $int_24 >>= 6; } } //Make lowercase again and trim off the padding. return strtolower(rtrim($output, '=')); } 

    В основном происходит удаление избыточной информации, а затем сжатие 4 байтов в 3. Это достигается за счет эффективного 6-битного подмножества таблицы ascii. Это окно перемещается так, что смещение начинается с полезных символов и включает в себя все персонажи, которые вы сейчас используете.

    С помощью смещения, которое я использовал, вы можете использовать что угодно: от ASCII 38 до 102. Это дает вам результирующую строку в 30 байт , это 9-байтовое (24%) сжатие! К сожалению, вам нужно сделать его безопасным для URL (возможно, с base64), который возвращает его до 40 байт.

    Думаю, на данный момент вы вполне можете предположить, что вы достигли уровня «безопасности через неизвестность», необходимого для того, чтобы остановить 99,9% людей. Давайте продолжим, хотя, во вторую часть вашего вопроса

    поэтому пользователь не может догадаться, как получить большее изображение

    Можно утверждать, что это уже разрешено с помощью вышеизложенного, но то, что вам нужно сделать, это передать это через секрет на сервере, желательно с помощью php openssl . Следующий код показывает полный поток использования вышеперечисленных функций и шифрование:

     $method = 'AES-256-CBC'; $secret = base64_decode('tvFD4Vl6Pu2CmqdKYOhIkEQ8ZO4XA4D8CLowBpLSCvA='); $iv = base64_decode('AVoIW0Zs2YY2zFm5fazLfg=='); $input = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500'; var_dump($input); $compressed = compress($input); var_dump($compressed); $encrypted = openssl_encrypt($compressed, $method, $secret, false, $iv); var_dump($encrypted); $decrypted = openssl_decrypt($encrypted, $method, $secret, false, $iv); var_dump($decrypted); $decompressed = decompress($compressed); var_dump($decompressed); 

    Результат этого скрипта следующий:

     string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500" string(30) "<  (  tJ  @ xH  G&( %  %  xW" string(44) "xozYGselci9i70cTdmpvWkrYvGN9AmA7djc5eOcFoAM=" string(30) "<  (  tJ  @ xH  G&( %  %  xW" string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500" 

    Вы увидите весь цикл: сжатие> шифрование> base64 encode / decode> decryption> декомпрессии. Результат этого будет как можно ближе, как вы могли бы получить, на минимальной длине, которую вы могли бы получить.

    Все в стороне, я чувствую себя обязанным заключить это с тем фактом, что это только теоретическое, и об этом было неплохо подумать. Есть определенно лучшие способы добиться желаемого результата – я буду первым, кто признает, что мое решение немного абсурдно!

    Вместо того, чтобы кодировать url, как насчет вывода миниатюрной копии исходного изображения? Вот что я думаю:

    1) Создайте «карту» для php, называя ваши изображения (фактические имена файлов) с использованием случайных символов. Random_bytes – отличное место для начала.

    2) Вставьте нужное разрешение в пределах рандомизированной строки url из # 1.

    3) Используйте функцию imagecopyresampled, чтобы скопировать исходное изображение в разрешение, которое вы хотите вывести, прежде чем вывести его на устройство клиента.

    Так, например:

    1 – Пример имени файла (из bin2hex(random_bytes(6)) ): a1492fdbdcf2.jpg

    2 – Требуемое разрешение: 800×600. Моя новая ссылка может выглядеть так: http://myserver.com/?800a1492fdbdcf2600 или, может быть, http://myserfer.com/?a1492800fdbdc600f2 или, может быть, даже http://myserver.com/?800a1492fdbdcf2=600 зависимости от того, где я выбираю встроить разрешение в ссылку

    3 – PHP знал бы, что имя файла a1492fdbdcf2.jpg, возьмите его, используйте imagecopyresampled для копирования в требуемое разрешение и выведите его.

    РЕДАКТИРОВАТЬ

    Чтение из комментариев выше и ниже, вам нужно решение, чтобы скрыть реальный путь вашего парсера изображений, придав ему фиксированную ширину изображения.

    Шаг 1: http://www.example.com/tn/full/animals/images/lion.jpg

    Вы можете получить базовый «thumbnailer», используя прибыль .htaccess

      RewriteEngine on RewriteBase / RewriteCond %{REQUEST_FILENAME} !-f RewriteRule tn/(full|small)/(.*) index.php?size=$1&img=$2 [QSA,L] 

    Ваш PHP-файл:

      $basedir="/public/content/"; $filename=realpath($basedir.$_GET["img"]); ## check that file is in $basedir if ((!strncmp($filename, $basedir, strlen($basedir)) ||(!file_exists($filename)) die("Bad file path"); switch ($_GET["size"]) { case "full": $width=700; $height=500; ## you can also use getimagesize() to test if the image is landscape or portrait break; default: $width=350; $height=250; break; } ## here is your old code for resizing images ## Note that the "tn" directory can exist and store the actual reduced images 

    Это позволяет использовать URL-адрес www.example.com/tn/full/animals/images/lion.jpg для просмотра уменьшенного размера изображения.

    Это имеет преимущество для SEO, чтобы сохранить исходное имя файла.

    Шаг 2: http://www.example.com/tn/full/lion.jpg

    Если вам нужен более короткий URL-адрес, если количество изображений у вас не слишком много, вы можете использовать базовое имя файла (например, «lion.jpg») и рекурсивно искать. При столкновении используйте индекс для определения того, который вы хотите (например, «1 – lion.jpg»)

     function matching_files($filename, $base) { $directory_iterator = new RecursiveDirectoryIterator($base); $iterator = new RecursiveIteratorIterator($directory_iterator); $regex_iterator = new RegexIterator($iterator, "#$filename\$#"); $regex_iterator->setFlags(RegexIterator::USE_KEY); return array_map(create_function('$a', 'return $a->getpathName();'), iterator_to_array($regex_iterator, false)); } function encode_name($filename) { $files=matching_files(basename($filename), realpath('public/content')); $tot=count($files); if (!$tot) return NULL; if ($tot==1) return $filename; return "/tn/full/".array_search(realpath($filename), $files)."--".basename($filename); } function decode_name($filename) { $i=0; if (preg_match("#^([0-9]+)--(.*)#", $filename, $out)) { $i=$out[1]; $filename=$out[2]; } $files=matching_files($filename, realpath('public/content')); return $files ? $files[$i] : NULL; } echo $name=encode_name("gallery/animals/images/lion.jp‌​g").PHP_EOL; ## --> returns lion.jpg ## You can use with the above solution the url http://www.example.com/tn/lion.jpg echo decode_name(basename($name)).PHP_EOL; ## -> returns the full path opn disk to the image "lion.jpg" 

    Оригинальное сообщение:

    В принципе, если вы добавите некоторое форматирование в свой пример, ваш сокращенный URL-адрес на самом деле длиннее:

     img=/dir/dir/hi-res-img.jpg&w=700&h=500 // 39 chars y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA // 50 chars 

    Использование base64_encode всегда приведет к более длинным строкам. И gzcompress потребует меньше, чтобы сохранить одно появление разных символов; это нехорошее решение для небольших струн.

    Таким образом, ничего не делать (или просто str_rot13 ), очевидно, является первым вариантом для рассмотрения, если вы хотите сократить результат, который у вас был ранее.

    Вы также можете использовать простой способ замены символов по вашему выбору:

      $raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500'; $from="0123456789abcdefghijklmnopqrstuvwxyz&=/ABCDEFGHIJKLMNOPQRSTUVWXYZ"; // the following line if the result of str_shuffle($from) $to="0IQFwAKU1JT8BM5npNEdi/DvZmXuflPVYChyrL4R7xc&SoG3Hq6ks=e9jW2abtOzg"; echo strtr($raw_query_string, $from, $to)."\n"; // Result: EDpL4MEu4MEu4NE-u5f-EDp.dmprYLU00rNLA00 // 39 chars 

    Чтение из вашего комментария, что вы действительно хотите, – «чтобы кто-нибудь не получил изображение привет-Res».

    Лучший способ добиться этого – создать контрольную сумму с помощью закрытого ключа.

    Encode:

     $secret="ujoo4Dae"; $raw_query_string = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500'; $encoded_query_string = $raw_query_string."&k=".hash("crc32", $raw_query_string.$secret); 

    Результат: img=/dir/dir/hi-res-img.jpg&w=700&h=500&k=2ae31804

    Decode:

     if (preg_match("#(.*)&k=([^=]*)$#", $encoded_query_string, $out) && (hash("crc32", $out[1].$secret) == $out[2])) { $decoded_query_string=$out[1]; } 

    Это не скрывает исходный путь, но у этого пути нет причин быть общедоступными, ваш «index.php» может выводить ваше изображение из локального каталога после проверки ключа.

    Если вы действительно хотите сократить исходный URL-адрес, вы должны учитывать допустимые символы в исходном URL-адресе. Многие методы сжатия основаны на том, что вы можете использовать полный байт, чтобы хранить больше, чем символ.

    Существует множество способов сокращения URL-адресов. Вы можете посмотреть, как другие сервисы, такие как TinyUrl, сокращают свои URL-адреса. Вот хорошая статья о хэшах и сокращения URL-адресов: http://blog.codinghorror.com/url-shortening-hashes-in-practice/

    Вы можете использовать функцию php mhash () для применения хэшей к строкам: http://php.net/manual/en/function.mhash.php

    И если вы прокрутите вниз до «Доступные хэши» на веб-сайте mhash, вы увидите, какие хэши вы можете использовать в функции (хотя я бы проверял, какие версии php имеют какие функции): http://mhash.sourceforge.net/mhash .3.html

    Я думаю, это было бы лучше сделано, не затеняя вообще. Вы могли бы просто кэшировать возвращенные изображения и использовать обработчик для их предоставления. Для этого требуется, чтобы размеры изображений были жестко закодированы в скрипт php. Когда вы получаете новые размеры, вы можете просто удалить все в кеше, поскольку он «ленивый загружен».

    1. Получить изображение из запроса
    Это может быть так: /thumbnail.php?image=img.jpg&album=myalbum . Можно даже сделать что-нибудь, используя переписать и иметь URL-адрес, например: /gallery/images/myalbum/img.jpg .

    2. Проверьте, не существует ли временная версия
    Вы можете сделать это, используя is_file() .

    3. Создайте его, если он не существует.
    Используйте текущую логику изменения размера, чтобы сделать это, но не выводите изображение. Сохраните его в временном расположении.

    4. Прочитайте содержимое временного файла в потоке
    В значительной степени просто вывести его.

    Вот пример непроверенного кода …

     <?php // assuming we have a request /thumbnail.php?image=img.jpg&album=myalbum // these are temporary filenames places. you need to do this yourself on your system. $image = $_GET['image']; // the file name $album = $_GET['album']; // the album $temp_folder = sys_get_temp_dir(); // temp dir to store images // (this should really be a specific cache path) $image_gallery = "images"; // root path to the image gallery $width = 700; $height = 500; $real_path = "$image_gallery/$album/$image"; $temp_path = "$temp_folder/$album/$image"; if(!is_file($temp_path)) { // read in the image $contents = file_get_contents($real_path); // resize however you are doing it now. $thumb_contents = resizeImage($contents, $width, $height); // write to temp file_put_contents($temp_path, $thumb_contents); } $type = 'image/jpeg'; header('Content-Type:'.$type); header('Content-Length: ' . filesize($temp_path)); readfile($temp_path); ?> 

    Краткие слова о «безопасности»

    Вы просто не сможете защитить свою ссылку, если нет секретного пароля, хранящегося где-то: пока URI несет всю информацию для доступа к вашему ресурсу, тогда она будет декодируемой и ваша «специальная безопасность» (они противоположны слова btw) будут легко разбиты.

    Вы можете поместить соль в свой PHP-код (например $mysalt="....long random string..." ), так как я сомневаюсь, что вы хотите вечную безопасность (такой подход слабый, потому что вы не можете обновить значение $mysalt , но в вашем случае достаточно информации о безопасности в течение нескольких лет, так как в любом случае пользователь может купить одну фотографию и поделиться ею в другом месте, нарушив любой из ваших механизмов безопасности).

    Если вы хотите иметь безопасный механизм, используйте хорошо известный (как инфраструктуру), наряду с механизмом проверки подлинности и прав пользователей (чтобы вы знали, кто ищет ваш образ, и разрешено ли им).

    Безопасность имеет стоимость, если вы не хотите позволить себе требования к вычислению и хранению, а затем забудьте об этом.


    Защитить, подписав URL-адрес

    Если вы хотите, чтобы пользователи избегали простого обхода и получения полного изображения res, вы можете просто подписать URI (но на самом деле, для безопасности, используйте что-то, что уже существует, а не пример быстрого примера ниже):

     $salt = '....long random stirng...'; $params = array('img' => '...', 'h' => '...', 'w' => '...'); $p = http_build_query($params); $check = password_hash($p, PASSWORD_BCRYPT, array('salt' => $salt, 'cost' => 1000); $uri = http_build_query(array_merge($params, 'sig' => $check)); 

    Декодирование:

     $sig = $_GET['sig']; $params = $_GET; unset($params['sig']); // Same as previous $salt = '....long random stirng...'; $p = http_build_query($params); $check = password_hash($p, PASSWORD_BCRYPT, array('salt' => $salt, 'cost' => 1000); if ($sig !== $check) throw new DomainException('Invalid signature'); не $sig = $_GET['sig']; $params = $_GET; unset($params['sig']); // Same as previous $salt = '....long random stirng...'; $p = http_build_query($params); $check = password_hash($p, PASSWORD_BCRYPT, array('salt' => $salt, 'cost' => 1000); if ($sig !== $check) throw new DomainException('Invalid signature'); 

    См. http://php.net/manual/fr/function.password-hash.php


    Укоротить

    «Сокращение» с помощью общего алгоритма сжатия бесполезно здесь, потому что заголовки будут длиннее URI, поэтому он почти никогда не сократит его.

    Если вы хотите сократить его, будьте умны: не указывайте относительный путь ( /dir/dir ), если он всегда один и тот же (или отдайте его, только если он не является основным). Не указывайте расширение, если оно всегда одно и то же (или дайте его, если оно не png если почти все находится в png ). Не указывайте height потому что изображение имеет aspect ratio : вам нужна только width . Дайте его в x100px если вам не нужна четкая пиксельная ширина.

    В вашем вопросе вы заявляете, что он должен быть чистым PHP и не использовать базу данных, и должна быть возможность декодировать строки. Так немного сгибать правила:

    • То, как я интерпретирую этот вопрос, состоит в том, что мы не очень заботимся о безопасности, но нам нужны самые короткие хэши, которые возвращаются к изображениям.
    • Мы также можем использовать «возможность декодирования» с щепоткой соли, используя алгоритм хэширования в один конец.
    • Мы можем хранить хэши внутри объекта JSON, а затем хранить данные в файле, поэтому все, что мы должны сделать в конце дня, это соответствие строк

    “ `

     class FooBarHashing { private $hashes; private $handle; /** * In producton this should be outside the web root * to stop pesky users downloading it and geting hold of all the keys. */ private $file_name = './my-image-hashes.json'; public function __construct() { $this->hashes = $this->get_hashes(); } public function get_hashes() { // Open or create a file. if (! file_exists($this->file_name)) { fopen($this->file_name, "w"); } $this->handle = fopen($this->file_name, "r"); $hashes = []; if (filesize($this->file_name) > 0) { $contents = fread($this->handle, filesize($this->file_name)); $hashes = get_object_vars(json_decode($contents)); } return $hashes; } public function __destroy() { // Close the file handle fclose($this->handle); } private function update() { $handle = fopen($this->file_name, 'w'); $res = fwrite($handle, json_encode($this->hashes)); if (false === $res) { //throw new Exception('Could not write to file'); } return true; } public function add_hash($image_file_name) { $new_hash = md5($image_file_name, false); if (! in_array($new_hash, array_keys($this->hashes) ) ) { $this->hashes[$new_hash] = $image_file_name; return $this->update(); } //throw new Exception('File already exists'); } public function resolve_hash($hash_string='') { if (in_array($hash_string, array_keys($this->hashes))) { return $this->hashes[$hash_string]; } //throw new Exception('File not found'); } } 

    “ `

    Пример использования:

     <?php // Include our class require_once('FooBarHashing.php'); $hashing = new FooBarHashing; // You will need to add the query string you want to resolve first. $hashing->add_hash('img=/dir/dir/hi-res-img.jpg&w=700&h=500'); // Then when the user requests the hash the query string is returned. echo $hashing->resolve_hash('65992be720ea3b4d93cf998460737ac6'); 

    Таким образом, конечный результат – это строка длиной всего 32 символа, что намного короче, чем у нас раньше.

    Боюсь, вы не сможете сократить строку запроса лучше, чем любой известный алгоритм сжатия. Как уже упоминалось, сжатая версия будет короче несколькими (около 4-6) символами, чем оригинал. Более того, исходная строка может быть декодирована относительно легко (против декодирования sha1 или md5, например).

    Я предлагаю сократить URL-адреса с помощью конфигурации веб-сервера. Вы можете укоротить его, заменив путь изображения идентификатором (пары ID-файла хранения в базе данных).

    Например, следующая конфигурация Nginx принимает URL-адреса, такие как /t/123456/700/500/4fc286f1a6a9ac4862bdd39a94a80858 , где

    • первое число ( 123456 ) должно быть идентификатором изображения из базы данных;
    • 700 и 500 – размеры изображения;
    • последняя часть – хеш MD5, защищающий от запросов с разными значениями .
     # Adjust maximum image size # image_filter_buffer 5M; server { listen 127.0.0.13:80; server_name img-thumb.local; access_log /var/www/img-thumb/logs/access.log; error_log /var/www/img-thumb/logs/error.log info; set $root "/var/www/img-thumb/public"; # /t/image_id/width/height/md5 location ~* "(*UTF8)^/t/(\d+)/(\d+)/(\d+)/([a-zA-Z0-9]{32})$" { include fastcgi_params; fastcgi_pass unix:/tmp/php-fpm-img-thumb.sock; fastcgi_param QUERY_STRING image_id=$1&w=$2&h=$3&hash=$4; fastcgi_param SCRIPT_FILENAME /var/www/img-thumb/public/t/resize.php; image_filter resize $2 $3; error_page 415 = /empty; break; } location = /empty { empty_gif; } location / { return 404; } } 

    Сервер принимает только URL-адреса указанного шаблона, пересылает запрос в скрипт /public/t/resize.php с измененной строкой запроса, а затем изменяет /public/t/resize.php изображения, сгенерированного PHP, с помощью модуля image_filter . В случае ошибки возвращает пустой GIF-образ.

    image_filter является обязательным, он включен только в качестве примера. Изменение размера может быть выполнено полностью на стороне PHP. Кстати, с Nginx можно избавиться от части PHP.

    PHP-скрипт должен проверять хэш следующим образом:

     // Store this in some configuration file. $salt = '^sYsdfc_sd&9wa.'; $w = $_GET['w']; $h = $_GET['h']; $true_hash = md5($w . $h . $salt . $image_id); if ($true_hash != $_GET['hash']) { die('invalid hash'); } $filename = fetch_image_from_database((int)$_GET['image_id']); $img = imagecreatefrompng($filename); header('Content-Type: image/png'); imagepng($img); imagedestroy($img); 

    Из обсуждения в разделе комментариев это похоже на то, что вы действительно хотите, чтобы защитить свои оригинальные изображения привет-res.

    Имея это в виду, я предлагаю на самом деле сделать это сначала, используя конфигурацию вашего веб-сервера (например, Apache mod_authz_core или Nginx ngx_http_access_module ), чтобы запретить доступ из Интернета в каталог, где хранятся ваши исходные изображения.

    Обратите внимание, что сервер будет отказывать только в доступе к вашим изображениям из Интернета, но вы все равно сможете получить к ним доступ непосредственно из ваших php-скриптов. Поскольку вы уже показываете изображения с помощью какого-то сценария «resizer», я предлагаю установить там какой-то жесткий предел и отказаться изменять размеры изображений на что-нибудь большее, чем это (например, что-то вроде этого $width = min(1000, $_GET['w']) ).

    Я знаю, что это не отвечает на ваш первоначальный вопрос, но я думаю, что это будет правильным решением для защиты ваших изображений. И если вы все же хотите обфускать исходное имя и изменить параметры, вы можете это сделать, но вы считаете нужным, не беспокоясь о том, что кто-то может понять, что стоит за ним.

    Я не думаю, что полученный URL-адрес можно сократить гораздо больше, чем на вашем собственном примере. Но я предлагаю несколько шагов, чтобы лучше обфускать ваши изображения.

    Сначала я удалю все, что вы можете, с базового url, который вы используете, и base64encoding, поэтому вместо

    IMG = / DIR / DIR / Hi-Res-IMG.jpg & W = 700 & ч = 500

    я хотел бы использовать

    s = привет разрешения-IMG.jpg, 700,500,062c02153d653119

    Если эти последние 16 символов являются хешем для проверки открываемого URL-адреса, это то же, что вы предложили в своем коде, – и пользователь не пытается обмануть высокое разрешение изображения из системы.

    Ваш index.php, который служит для изображений, начнется следующим образом:

     function myHash($sRaw) { // returns 16 chars dual hash return hash('adler32', $sRaw) . strrev(hash('crc32', $sRaw)); } // These 2 hash algos are suggestions, there are more for you to chose. // s=hi-res-img.jpg,700,500,062c02153d653119 $aParams = explode(',', $_GET['s']); if (count($aParams) != 4) { die('Invalid call.'); } list($sFileName, $iWidth, $iHeight, $sHash) = $aParams; $sRaw = session_id() . $sFileName . $iWidth . $iHeight; if ($sHash != myHash($sRaw)) { die('Invalid hash.'); } 

    После этого момента вы можете отправить изображение, когда пользователь открыл доступ к действительной ссылке.

    Обратите внимание на использование session_id как части необработанной строки, которая делает хеш необязательным, но не позволит пользователям использовать действительный URL-адрес, поскольку это будет привязка к сеансу. Если вы хотите, чтобы URL-адреса были доступны для совместного использования, просто удалите session_id из этого вызова.

    Я бы обернул полученный URL так же, как вы уже сделали, zip + base64. Результат будет даже больше, чем ваша версия, но более сложно увидеть через обфускацию и, следовательно, защитить ваши изображения от несанкционированных загрузок.

    Если вы хотите сделать это короче, я не вижу способа сделать это без переименования файлов (или их папок) или без использования базы данных.

    Предложенное решение для базы данных файлов, безусловно, создаст проблемы параллелизма – если у вас всегда не будет или мало кто использует систему одновременно.

    Вы говорите, что хотите, чтобы размер был таким, чтобы, если вы однажды решите, что изображения предварительного просмотра слишком малы, вы хотите увеличить размер – решение здесь состоит в том, чтобы жестко закодировать размер изображения в сценарии php и устранить его из URL-адреса , Если вы хотите изменить размер в будущем, измените жестко заданные значения в скрипте php (или в файле config.php, который вы включите в скрипт).

    Вы также сказали, что уже используете файлы для хранения данных изображения как объекта JSON, например: name , title , description . Используя это, вам не нужна база данных и вы можете использовать имя файла JSON в качестве ключа для поиска данных изображения.

    Когда пользователь посещает URL-адрес, например:

     www.mysite.com/share/index.php?ax9v 

    Вы загружаете ax9v.json из местоположения, в котором вы уже храните файлы json, и в этом json-файле сохраняется реальный путь изображения. Затем загрузите изображение, измените его размер в соответствии с жестким кодом в вашем скрипте и отправьте его пользователю.

    Исходя из выводов в https://blog.codinghorror.com/url-shortening-hashes-in-practice/ , чтобы получить наименьшую часть строки поиска в URL-адресе, вам потребуется повторить правильные комбинации символов при загрузке новых файлов ( например, первый – это «AAA», затем «AAB», «AAC» и т. д.) вместо использования алгоритма хэширования. Тогда ваше решение будет содержать только 3 символа для первых 238 328 фотографий, которые вы загружаете.

    Я начал прототип php-решения на phpfiddle, но код исчез (не используйте phpfiddle).

    Много говорилось о том, как кодирование не помогает безопасности, поэтому я просто концентрируюсь на сокращении и эстетике.

    Вместо того, чтобы думать о ней как о строчке, вы можете рассматривать ее как 3 отдельных компонента. Затем, если вы ограничите свое пространство кода для каждого компонента, вы можете упаковать вещи намного меньше.

    Например

    • path – Только состоящий из 26 символов (az) и / -. (Переменная длина)
    • width – Integer (0 – 65k) (Фиксированная длина, 16 бит)
    • height – Integer (0 – 65k) (Фиксированная длина, 16 бит)

    Я ограничиваю путь только состоянием максимум 31 символа, поэтому мы можем использовать 5-битные группировки.

    Сначала установите размеры фиксированной длины и добавьте каждый символ пути как 5 бит. Также может потребоваться добавить специальный нулевой символ для заполнения конечного байта. Очевидно, вам нужно использовать ту же строку словаря для кодирования и декодирования.

    См. Код ниже.

    Это показывает, что, ограничивая кодирование и сколько вы можете кодировать, вы можете получить более короткую строку. Вы можете сделать это еще короче, используя только 12-разрядные целые числа (макс. 2048) или даже удаление частей пути, если они известны, например, базовый путь или расширение файла (см. Последний пример).

     <?php function encodeImageAndDimensions($path, $width, $height) { $dictionary = str_split("abcdefghijklmnopqrstuvwxyz/-."); //Max 31 chars please if ($width >= pow(2,16)) { throw new Exception("Width value is too high to encode with 16 bits"); } if ($height >= pow(2,16)) { throw new Exception("Height value is too high to encode with 16 bits"); } //Pack width, then height first $packed = pack("nn", $width, $height); $path_bits = ""; foreach (str_split($path) as $ch) { $index = array_search($ch, $dictionary, true); if ($index === false) { throw new Exception("Cannot encode character outside of the allowed dictionary"); } $index++; //Add 1 due to index 0 meaning NULL rather than a. //Work with a bit string here rather than using complicated binary bit shift operators. $path_bits .= str_pad(base_convert($index, 10, 2), 5, "0", STR_PAD_LEFT); } //Remaining space left? $modulo = (8 - (strlen($path_bits) % 8)) %8; if ($modulo >=5) { //There is space for a null character to fill up to the next byte $path_bits .= "00000"; $modulo -= 5; } //Pad with zeros $path_bits .= str_repeat("0", $modulo); //Split in to nibbles and pack as a hex string $path_bits = str_split($path_bits, 4); $hex_string = implode("", array_map(function($bit_string) { return base_convert($bit_string, 2, 16); }, $path_bits)); $packed .= pack('H*', $hex_string); return base64_url_encode($packed); } function decodeImageAndDimensions($str) { $dictionary = str_split("abcdefghijklmnopqrstuvwxyz/-."); $data = base64_url_decode($str); $decoded = unpack("nwidth/nheight/H*path", $data); $path_bit_stream = implode("", array_map(function($nibble) { return str_pad(base_convert($nibble, 16, 2), 4, "0", STR_PAD_LEFT); }, str_split($decoded['path']))); $five_pieces = str_split($path_bit_stream, 5); $real_path_indexes = array_map(function($code) { return base_convert($code, 2, 10) - 1; }, $five_pieces); $real_path = ""; foreach ($real_path_indexes as $index) { if ($index == -1) { break; } $real_path .= $dictionary[$index]; } $decoded['path'] = $real_path; return $decoded; } //These do a bit of magic to get rid of the double equals sign and obfuscate a bit. It could save an extra byte. function base64_url_encode($input) { $trans = array('+' => '-', '/' => ':', '*' => '$', '=' => 'B', 'B' => '!'); return strtr(str_replace('==', '*', base64_encode($input)), $trans); } function base64_url_decode($input) { $trans = array('-' => '+', ':' => '/', '$' => '*', 'B' => '=', '!' => 'B'); return base64_decode(str_replace('*', '==',strtr($input,$trans))); } //Example usage $encoded = encodeImageAndDimensions("/dir/dir/hi-res-img.jpg", 700, 500); var_dump($encoded); // string(27) "Arw!9NkTLZEy2hPJFnxLT9VA4A$" $decoded = decodeImageAndDimensions($encoded); var_dump($decoded); // array(3) { ["width"]=> int(700) ["height"]=> int(500) ["path"]=> string(23) "/dir/dir/hi-res-img.jpg" } $encoded = encodeImageAndDimensions("/another/example/image.png", 4500, 2500); var_dump($encoded); // string(28) "EZQJxNhc-iCy2XAWwYXaWhOXsHHA" $decoded = decodeImageAndDimensions($encoded); var_dump($decoded); // array(3) { ["width"]=> int(4500) ["height"]=> int(2500) ["path"]=> string(26) "/another/example/image.png" } $encoded = encodeImageAndDimensions("/short/eg.png", 300, 200); var_dump($encoded); // string(19) "ASwAyNzQ-VNlP2DjgA$" $decoded = decodeImageAndDimensions($encoded); var_dump($decoded); // array(3) { ["width"]=> int(300) ["height"]=> int(200) ["path"]=> string(13) "/short/eg.png" } $encoded = encodeImageAndDimensions("/very/very/very/very/very-hyper/long/example.png", 300, 200); var_dump($encoded); // string(47) "ASwAyN2LLO7FlndiyzuxZZ3Yss8Rm!ZbY9x9lwFsGF7!xw$" $decoded = decodeImageAndDimensions($encoded); var_dump($decoded); // array(3) { ["width"]=> int(300) ["height"]=> int(200) ["path"]=> string(48) "/very/very/very/very/very-hyper/long/example.png" } $encoded = encodeImageAndDimensions("only-file-name", 300, 200); var_dump($encoded); //string(19) "ASwAyHuZnhksLxwWlA$" $decoded = decodeImageAndDimensions($encoded); var_dump($decoded); // array(3) { ["width"]=> int(300) ["height"]=> int(200) ["path"]=> string(14) "only-file-name" } 

    теория

    In theory we need a short input character set and a large output character set. I will demonstrate it by the following example. We have the number 2468 as integer with 10 characters (0-9) as character set. We can convert it to the same number with base 2 (binary number system). Then we have a shorter character set (0 and 1) and the result is longer: 100110100100

    But if we convert to hexadecimal number (base 16) with a character set of 16 (0-9 and AF). Then we get a shorter result: 9A4

    практика

    So in your case we have the following character set for the input:

     $inputCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyz=/-.&"; 

    In total 41 characters: Numbers, lower cases and the special chars = / – . &

    The character set for output is a bit tricky. We want use URL save characters only. I've grabbed them from here: Characters allowed in GET parameter

    So our output character set is (73 characters):

     $outputCharacterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-_.!*'(),$"; 

    Numbers, lower AND upper cases and some special chars.

    We have more characters in our set for the output than for the intput. Theory says we can short our input string. CHECK!

    Coding

    Now we need an encode function from base 41 to base 73. For that case I don't know a PHP function. Luckily we can grab the function 'convBase' from here: http://php.net/manual/de/function.base-convert.php#106546 (if someone knows a smarter function let me know)

     <?php function convBase($numberInput, $fromBaseInput, $toBaseInput) { if ($fromBaseInput==$toBaseInput) return $numberInput; $fromBase = str_split($fromBaseInput,1); $toBase = str_split($toBaseInput,1); $number = str_split($numberInput,1); $fromLen=strlen($fromBaseInput); $toLen=strlen($toBaseInput); $numberLen=strlen($numberInput); $retval=''; if ($toBaseInput == '0123456789') { $retval=0; for ($i = 1;$i <= $numberLen; $i++) $retval = bcadd($retval, bcmul(array_search($number[$i-1], $fromBase),bcpow($fromLen,$numberLen-$i))); return $retval; } if ($fromBaseInput != '0123456789') $base10=convBase($numberInput, $fromBaseInput, '0123456789'); else $base10 = $numberInput; if ($base10<strlen($toBaseInput)) return $toBase[$base10]; while($base10 != '0') { $retval = $toBase[bcmod($base10,$toLen)].$retval; $base10 = bcdiv($base10,$toLen,0); } return $retval; } 

    Now we can short the url. Конечный код:

     $input = 'img=/dir/dir/hi-res-img.jpg&w=700&h=500'; $inputCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyz=/-.&"; $outputCharacterSet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz~-_.!*'(),$"; $encoded = convBase($input, $inputCharacterSet, $outputCharacterSet); var_dump($encoded); // string(34) "BhnuhSTc7LGZv.h((Y.tG_IXIh8AR.$!t*" $decoded = convBase($encoded, $outputCharacterSet, $inputCharacterSet); var_dump($decoded); // string(39) "img=/dir/dir/hi-res-img.jpg&w=700&h=500" 

    The encoded string has only 34 characters.

    Оптимизации

    You can optimize the count of characters by

    • reduce the length of input string. Do you really need the overhead of url parameter syntax? Maybe you can format your string as follows:

      $input = '/dir/dir/hi-res-img.jpg,700,500';

      This reduces the input itself AND the input character set. Your reduced input character set is then:

      $inputCharacterSet = "0123456789abcdefghijklmnopqrstuvwxyz/-.,";

      Final output:

      string(27) "E$AO.Y_JVIWMQ9BB_Xb3!Th*-Ut"

      string(31) "/dir/dir/hi-res-img.jpg,700,500"

    • reducing the input character set ;-). Maybe you can exclude some more characters? You can encode the numbers to characters first. Then your input character set can be reduced by 10!

    • increase your output character set. So the given set by me is googled within 2 minutes. Maybe you can use more url save characters. No idea… Maybe someone has a list

    Безопасность

    Heads up: There is no cryptographically logic in the code. So if somebody guesses the character sets, he can decode the string easily. But you can shuffle the character sets (once). Then it is a bit harder for the attacker, but not really safe. Maybe its enough for your use case anyway.