Кратчайшая возможная кодированная строка с возможностью декодирования (shorten 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 & h=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?

13 ответов


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

Часть первая:

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

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

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

Non-gzipped:string(52) "aW1nPS9kaXIvZGlyL2hpLXJlcy1pbWcuanBnJnc9NzAwJmg9NTAw"

Gzipped:string(52) "y8xNt9VPySwC44xM3aLUYt3M3HS9rIJ0tXJbcwMDtQxbUwMDAA=="

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

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

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, i.e 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% людей. Давайте продолжим, хотя, ко второй части вашего вопрос

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

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

$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 > дешифрование > декомпрессия. Выход этого будет как можно ближе, насколько это возможно, на самой короткой длине, которую вы можете получить.


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

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

2) вставьте нужное разрешение в рандомизированную строку url из #1.

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

например:

1 - пример имени файла (с bin2hex(random_bytes(6))): a1492fdbdcf2.формат JPG

2-желаемое разрешение: 800x600. Моя новая ссылка может выглядеть так: 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

вы можете достичь базового "миниатюриста", взяв прибыль .htaccess

 RewriteEngine on
 RewriteBase /
 RewriteCond %{REQUEST_FILENAME} !-f
 RewriteRule tn/(full|small)/(.*) index.php?size=&img= [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, если количество изображений у вас не слишком много, вы можете использовать базовое имя файла (например. "лев.jpg") и рекурсивный поиск. При столкновении используйте индекс, чтобы определить, какой из них вы хотите (например. "1--лев.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

чтение из вашего комментария, то, что вы действительно хотите, это "чтобы никто не получил изображение hi-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

расшифрую:

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

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

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


есть много способов сократить URL-адреса. Вы можете посмотреть, как другие службы, такие как TinyUrl, сокращают свои URL-адреса. Вот хорошая статья о хэшах и сокращениях urls: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. Создайте его если он не существует
Для этого используйте текущую логику изменения размера, но не выводите изображение. Сохраните его в месте temp.

4. Прочитайте содержимое временного файла в stream
Просто выход его.

здесь непроверенные код образец...

<?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...") поскольку я сомневаюсь, что вы хотите вечной безопасности (такой подход слаб, потому что вы не можете обновить the $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');

посмотреть 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 chars длинные, что намного короче, чем 52 у нас было раньше.


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

Я предлагаю сократить URL-адреса с помощью конфигурации веб-сервера. Вы можете сократите его еще больше, заменив путь изображения на ID (store 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=&w=&h=&hash=;
    fastcgi_param  SCRIPT_FILENAME /var/www/img-thumb/public/t/resize.php;

    image_filter resize  ;
    error_page 415 = /empty;

    break;
  }

  location = /empty {
    empty_gif;
  }

  location / { return 404; }
}

сервер принимает только URL-адреса указанного шаблона, перенаправляет запрос на /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);

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

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

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

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


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

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

img=/dir/dir / hi-res-img.jpg & w=700 & h=500

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

s=hi-res-img.jpg, 700, 500, 062c02153d653119

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

ваш индекс.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(или в конфигурации.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 отдельных компонента. Затем, если вы ограничите пространство кода для каждого компонента, вы можете упаковать вещи вместе намного меньше.

Э. Г.

  • путь - только состоящий из 26 символов (a-z) и / - . (Переменная длина)
  • ширина - целое число (0-65k) (фиксированная длина, 16 бит)
  • высота - целое число (0-65k) (фиксированная длина, 16 бит)

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

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

см. код ниже.

Это показывает, что, ограничивая то, что вы кодируете и сколько вы можете кодировать, вы можете получить более короткую строку. Вы можете сделать его еще короче, используя только 12-битные целые числа измерений (max 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" }

теория

теоретически нам нужен короткий входной набор символов и большой выходной набор символов. Я продемонстрирую это на следующем примере. У нас есть число 2468 как целое число с 10 символами (0-9) в качестве набора символов. Мы можем преобразовать его в тот же число с основанием 2 (двоичная система счисления). Тогда у нас есть короткий набор символов (0 и 1) и результат больше: 100110100100

но если мы преобразуем в шестнадцатеричное число (база 16) с набором символов 16 (0-9 и A-F). Тогда мы получим более короткий результат: 9A4

практика

Итак, в вашем случае у нас есть следующий набор символов для ввода:

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

всего 41 символ: цифры, строчные буквы и специальные символы =/ - . &

набор символов для вывода немного сложнее. Мы хотим использовать только символы сохранения URL. Я схватил их отсюда:символы, разрешенные в GET parameter

так наш характер выхода набор (73 символов):

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

номера, нижний и верхний регистры и некоторые специальные символы.

у нас больше символов в нашем наборе для вывода, чем для ввода. Теория говорит, что мы можем сократить нашу входную строку. Чек!

кодирование

теперь нам нужна функция кодирования от базы 41 до базы 73. В этом случае я не знаю функцию PHP. К счастью, мы можем захватить функцию "convBase" отсюда: http://php.net/manual/de/function.base-convert.php#106546 (если кто-то знает более умную функцию, дайте мне знать)

<?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;
}

теперь мы можем сократить 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"

закодированная строка содержит только 34 символа.

оптимизация

вы можете оптимизировать количество символов по

  • уменьшить длину входной строки. Вам действительно нужны накладные расходы url синтаксис параметров? Возможно, вы можете отформатировать строку следующим образом:

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

    это уменьшает сам вход и набор входных символов. Ваш уменьшенный набор входных символов:

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

    окончательный результат:

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

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

  • уменьшение набора входных символов; -). Может быть, вы можете исключить еще несколько персонажей? Сначала можно закодировать числа в символы. Тогда ваш входной набор символов может быть уменьшен на 10!

  • увеличить выходной набор символов. Таким образом, данный набор мной googled в течение 2 минут. Возможно, вы можете использовать больше символов сохранения url. Без понятия... Может быть, у кого-то есть список

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

Heads up: в коде нет криптографической логики. Поэтому, если кто-то угадывает наборы символов, он может легко декодировать строку. Но вы можете перетасовать наборы символов (однажды.) Тогда это немного сложнее для атакующего, но не совсем безопасно. Может быть, его достаточно для вашего использования в любом случае.