web-dev-qa-db-ja.com

PHP

文字列をshortest可能な長さにエンコードし、decodable(純粋な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)); 

エンコードされた結果を短縮し、それでもonly PHPを使用してデコードする可能性はありますか?

15
Artur Filipiak

ユーザーがデコードできないようにしたい場合は、ハッシュの方法についてもっと考える必要があると思います。 _base64_の問題は、base64文字列がbase64文字列のように見えることです。あなたのページのソースを見るのに十分な知識を持っている誰かが、おそらくそれを認識する可能性も十分にあります。

パート1:

文字列を可能な限り短い長さにエンコードするメソッド

URLの語彙や文字に柔軟性がある場合は、これが出発点として最適です。 gzipは後方参照を使用して多くの利点を得ているため、文字列が非常に短いため、ほとんど意味がありません。

あなたの例を考えてみましょう-圧縮で2バイトしか節約できませんでしたが、base64パディングで再び失われます:

Gzipされていない:string(52) "aW1nPS9kaXIvZGlyL2hpLXJlcy1pbWcuanBnJnc9NzAwJmg9NTAw"

Gzip圧縮: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, 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に圧縮されます。これは、ASCIIテーブルの6ビットのサブセットを効果的に使用することで実現されます。このウィンドウは、オフセットが有効な文字で始まり、現在使用しているすべての文字が含まれるように移動されます。

私が使用したオフセットを使用すると、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エンコード/デコード>復号化>解凍。これの出力は、あなたが得ることができる最も短い長さの近くで、あなたが実際に得ることができる限り可能な限り近いでしょう。

余談ですが、これは理論上のものであり、これは考えるのがいい挑戦でした。あなたの望ましい結果を達成するためのより良い方法は間違いなくあります-私の解決策が少しばかげていることを最初に認めます!

13
calcinai

URLをエンコードする代わりに、元の画像のサムネイルコピーを出力してみませんか?これが私が考えていることです:

1)ランダムな文字を使用して写真(実際のファイル名)に名前を付けて、phpの「マップ」を作成します。 Random_bytes は、始めるのに最適な場所です。

2)#1のランダム化されたURL文字列内に必要な解像度を埋め込みます。

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を使用して目的の解像度にコピーし、出力します。

4
JDW

[〜#〜]編集[〜#〜]

上記と以下のコメントを読むと、画像パーサーの実際のパスを隠し、固定の画像幅を与えるソリューションが必要です。

ステップ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=$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

これにより、www.example.com/tn/full/animals/images/lion.jpgというURLを使用して、縮小した画像を表示できます。

これには、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は、異なるcharの1つの出現を格納するために必要な量が少なくなります。これは小さな文字列には適したソリューションではありません。

したがって、以前に行った結果を短くしたい場合は、何もしない(または単純な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

コメントから読んで、あなたが本当に望んでいるのは「誰もが高解像度の画像を取得するのを防ぐこと」です。

これを実現する最良の方法は、秘密鍵を使用してチェックサムを生成することです。

エンコード:

$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];
}

これは元のパスを隠しませんが、このパスが公開される理由はありません。キーが確認されると、「index.php」はローカルディレクトリから画像を出力できます。

元のURLを本当に短くしたい場合は、元のURLで使用できる文字を制限することを検討する必要があります。多くの圧縮方法は、1バイトを使用して1文字以上を格納できるという事実に基づいています。

3
Adam

これは、何も隠さない方がいいでしょう。返された画像を簡単にキャッシュし、ハンドラーを使用してそれらを提供できます。これには、画像サイズをphpスクリプトにハードコードする必要があります。新しいサイズを取得したら、「遅延読み込み」されているため、キャッシュ内のすべてを削除できます。

1。リクエストから画像を取得
これは次の可能性があります:_/thumbnail.php?image=img.jpg&album=myalbum_。それは、書き換えを使用して何かにすることもでき、_/gallery/images/myalbum/img.jpg_のようなURLを持つこともできます。

2。一時バージョンが存在しないかどうかを確認します
これは、is_file()を使用して行うことができます。

。存在しない場合は作成します
現在のサイズ変更ロジックを使用してそれを行いますが、画像は出力しません。一時的な場所に保存します。

4。一時ファイルの内容をストリームに読み込みます
かなりそれを出力します。

これがnestedコードの例です...

_<?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);
?>
_
2
Michael Coxon

「セキュリティ」についての短い言葉

どこかに「秘密のパスワード」が保存されていない場合、リンクを保護することはできません。URIがリソースにアクセスするためのすべての情報を保持している限り、それはデコード可能であり、「カスタムセキュリティ」になります(これらは逆です)単語btw)は簡単に壊れます。

永遠のセキュリティが必要だとは思えないため、PHPコード($mysalt="....long random string..."など)にソルトを挿入できます($mysaltを更新できないため、このようなアプローチは弱いです)価値がありますが、あなたの場合、ユーザーはとにかく1つの画像を購入して他の場所で共有し、セキュリティメカニズムを壊すことができるため、数年のセキュリティで十分です。

安全なメカニズムが必要な場合は、よく知られているメカニズムを(フレームワークが実行するように)使用し、さらに認証およびユーザー権利管理メカニズムを使用します(これにより、誰がイメージを探しているか、およびイメージが許可されているかどうかを確認できます)。

セキュリティにはコストがかかりますが、そのコンピューティングと保存の要件を許容したくない場合は、忘れてください。


URLに署名して保護する

ユーザーが簡単に迂回して完全な解像度の画像を取得したくない場合は、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でない場合に付けます)。画像にはaspect ratioが含まれているため、heightは指定しないでください。必要なのはwidthだけです。ピクセル単位の幅が必要ない場合は、x100pxで指定してください。

2
Xenos

URLを短くする方法はたくさんあります。 TinyUrlなどの他のサービスがURLを短縮する方法を調べることができます。ここにハッシュと短縮URLに関する良い記事があります: http://blog.codinghorror.com/url-shortening-hashes-in-practice/

Php関数mhash()を使用して、文字列にハッシュを適用できます: http://php.net/manual/en/function.mhash.php

そして、mhashのWebサイトで「利用可能なハッシュ」までスクロールすると、関数で使用できるハッシュを確認できます(ただし、どのバージョンのphpがどの関数を持っているかを確認します): http://mhash.sourceforge .net/mhash.3.html

2
GreeKatrina

あなたの質問では、それは純粋なPHPであり、データベースを使用してはならず、文字列をデコードする可能性があるはずです。そのため、ルールを少し曲げる必要があります。

  • 私がこの質問を解釈している方法は、セキュリティについてそれほど気にしないということですが、画像に戻る最短のハッシュが必要です。
  • また、一方向ハッシュアルゴリズムを使用することで、簡単なソルトで「デコードの可能性」をとることもできます。
  • JSONオブジェクト内にハッシュを格納してから、データをファイルに格納できるため、1日の終わりに行う必要があるのは文字列の照合だけです。

「」

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');

したがって、最終結果は2文字だけの文字列であり、以前の52よりもはるかに短いです。

1
Aron

申し訳ありませんが、既知の圧縮アルゴリズムよりもクエリ文字列を短くすることはできません。すでに述べたように、圧縮されたバージョンは、元のバージョンよりも数文字(約4〜6文字)短くなります。さらに、元の文字列は比較的簡単にデコードできます(たとえば、sha1やmd5のデコードとは異なります)。

Webサーバー構成を使用してURLを短くすることをお勧めします。イメージパスをIDに置き換えることで、さらに短くすることができます(データベースに(ID-filenameペアを格納))。

たとえば、次の Nginx 構成は/t/123456/700/500/4fc286f1a6a9ac4862bdd39a94a80858のようなURLを受け入れます。ここで、

  • 最初の番号(123456)は、データベースからのイメージIDであると想定されています。
  • 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スクリプトにリクエストを転送し、PHP with 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);
0
Ruslan Osmanov

コメントセクションの議論から、あなたが本当に望んでいるのはオリジナルの高解像度画像を保護することです。

それを念頭に置いて、私は実際に最初にあなたのウェブサーバー設定(例えばApache mod_authz_core またはNginx ngx_http_access_module )を使用してウェブからディレクトリへのアクセスを拒否することを実際に行うことをお勧めします元の画像が保存されます。

サーバーはWebからの画像へのアクセスのみを拒否しますが、phpスクリプトから直接画像にアクセスできることに注意してください。 「リサイザー」スクリプトを使用して画像をすでに表示しているので、そこにハードリミットを設定し、それよりも大きいものに画像のサイズを変更することを拒否することをお勧めします(たとえば、$width = min(1000, $_GET['w'])のようなもの)。

これで元の質問に答えられないことはわかっていますが、これは画像を保護するための適切な解決策になると思います。さらに、元の名前とサイズ変更パラメーターを難読化したい場合は、それを行うことができますが、背後にあるものを誰かが理解するかもしれないと心配することなく、適切と判断できます。

0
Giedrius D

プレビュー画像が小さすぎると判断した場合にサイズを大きくしたいので、そこにサイズが必要だと言います-ここでの解決策は、画像サイズをphpスクリプトにハードコードし、URLから削除することです。将来的にサイズを変更する場合は、phpスクリプト(またはスクリプトに含めるconfig.php)のハードコードされた値を変更します。

また、画像データをnametitledescriptionなどのJSONオブジェクトとして保存するために、すでにファイルを使用しているとのことですが、これを利用して、データベースは必要なく、JSONファイル名を画像データを検索するためのキーとして使用できます。

ユーザーが次のようなURLにアクセスすると、

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

すでにjsonファイルを保存している場所からax9v.jsonをロードし、そのjsonファイル内に画像の実際のパスが保存されます。次に、画像をロードし、スクリプトでハードコーディングされたサイズに従ってサイズを変更して、ユーザーに送信します。

https://blog.codinghorror.com/url-shortening-hashes-in-practice/ の結論から、有効な文字を反復処理する必要があるURLの最小の検索文字列部分を取得するハッシュアルゴリズムを使用する代わりに、新しいファイルがアップロードされるときの組み合わせ(たとえば、最初のファイルは「AAA」、「AAB」、「AAC」など)。ソリューションでは、アップロードした最初の238,328枚の写真の文字列が3文字だけになります。

私はphpfiddleでphpソリューションのプロトタイプを作成し始めましたが、コードが消えました(phpfiddleは使用しないでください)。

0
Luke

理論

理論的には、短い入力文字セットと大きな出力文字セットが必要です。以下の例で説明します。 2468という数値は、文字セットとして10文字(0〜9)の整数です。基数2(2進数システム)で同じ数に変換できます。次に、文字セットが短く(0と1)、結果は長くなります:100110100100

ただし、16の文字セット(0〜9およびA〜F)を使用して16進数(基数16)に変換するとします。次に、より短い結果を取得します:9A4

練習

したがって、あなたの場合、入力には次の文字セットがあります。

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

合計41文字:数字、小文字、特殊文字= /-。 &

出力用の文字セットは少しトリッキーです。 URL保存文字のみを使用します。私はここからそれらを取得しました: GETパラメーターで許可されている文字

したがって、出力文字セットは(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減らすことができます。

  • 出力文字セットを増やします。したがって、私が指定したセットは2分以内にグーグルされます。多分あなたはもっと多くのURL保存文字を使うことができます。わからない...多分誰かがリストを持っている

安全保障

ヘッドアップ:コードには暗号化ロジックはありません。したがって、誰かが文字セットを推測した場合、彼は文字列を簡単にデコードできます。ただし、文字セットを(1回だけ)シャッフルできます。攻撃者にとっては少し難しいですが、実際には安全ではありません。とにかく、ユースケースには十分でしょう。

0
Timo

エンコーディングがセキュリティにどのように役立たないかについては多くのことが言われているので、私は短縮と美学に集中しています。

これを文字列と考えるのではなく、3つの個別のコンポーネントと考えることができます。次に、各コンポーネントのコードスペースを制限すると、物事をはるかに小さくまとめることができます。

例えば。

  • path-26文字(a-z)および/-のみで構成されます。 (可変長)
  • width-整数(0-65k)(固定長、16ビット)
  • height-整数(0-65k)(固定長、16ビット)

5ビットのグループ化を使用できるように、パスを最大31文字のみに制限しています

最初に固定長の次元をパックし、各パス文字を5ビットとして追加します。終了バイトを埋めるために、特殊なnull文字を追加する必要がある場合もあります。明らかに、エンコードとデコードには同じ辞書文字列を使用する必要があります。

以下のコードを参照してください。

これは、エンコードする対象とエンコードできる量を制限することで、より短い文字列を取得できることを示しています。 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" }
0
Phil

結果のURLは、あなた自身の例よりもはるかに短縮できるとは思いません。しかし、画像をより難読化するためのいくつかの手順をお勧めします。

まず、圧縮しているベースURLとbase64encodingから可能な限りすべてを削除するので、代わりに

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

私は使うだろう

s = hi-res-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を共有できないことに注意してください。 RLを共有可能にする場合は、その呼び出しからsession_idを削除します。

結果のURLは、Zip + base64と同じようにラップします。結果はあなたのバージョンよりもさらに大きくなりますが、難読化を通して見るのがより難しくなり、そのため画像を不正なダウンロードから保護します。

短くするだけの場合、ファイル(またはそのフォルダー)の名前を変更せずに、またはデータベースを使用せずにそれを行う方法はありません。

提案されたファイルデータベースソリューションは、同時実行の問題を確実に引き起こします-同時にシステムを使用している人がいないか、非常に少ない場合を除きます。

0
Edelmar Ziegler