文字列をshortest可能な長さにエンコードし、decodable(純粋なPHP、SQLなし)にできるメソッドを探しています。動作するスクリプトはありますが、エンコードされた文字列の長さに満足できません。
シナリオ:
画像へのリンク(ユーザーに表示するファイル解像度によって異なります):
エンコードされたリンク(ユーザーが大きな画像を取得する方法を推測できないようにするため):
したがって、基本的にはURLの検索クエリ部分のみをエンコードしたいと思います。
私が現在使用しているメソッドは、上記のクエリ文字列を次のようにエンコードします。
私が使用する方法は次のとおりです。
$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を使用してデコードする可能性はありますか?
ユーザーがデコードできないようにしたい場合は、ハッシュの方法についてもっと考える必要があると思います。 _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エンコード/デコード>復号化>解凍。これの出力は、あなたが得ることができる最も短い長さの近くで、あなたが実際に得ることができる限り可能な限り近いでしょう。
余談ですが、これは理論上のものであり、これは考えるのがいい挑戦でした。あなたの望ましい結果を達成するためのより良い方法は間違いなくあります-私の解決策が少しばかげていることを最初に認めます!
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を使用して目的の解像度にコピーし、出力します。
[〜#〜]編集[〜#〜]
上記と以下のコメントを読むと、画像パーサーの実際のパスを隠し、固定の画像幅を与えるソリューションが必要です。
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が元のファイル名を保持できるという利点があります。
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.jpg").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文字以上を格納できるという事実に基づいています。
これは、何も隠さない方がいいでしょう。返された画像を簡単にキャッシュし、ハンドラーを使用してそれらを提供できます。これには、画像サイズを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);
?>
_
どこかに「秘密のパスワード」が保存されていない場合、リンクを保護することはできません。URIがリソースにアクセスするためのすべての情報を保持している限り、それはデコード可能であり、「カスタムセキュリティ」になります(これらは逆です)単語btw)は簡単に壊れます。
永遠のセキュリティが必要だとは思えないため、PHPコード($mysalt="....long random string..."
など)にソルトを挿入できます($mysalt
を更新できないため、このようなアプローチは弱いです)価値がありますが、あなたの場合、ユーザーはとにかく1つの画像を購入して他の場所で共有し、セキュリティメカニズムを壊すことができるため、数年のセキュリティで十分です。
安全なメカニズムが必要な場合は、よく知られているメカニズムを(フレームワークが実行するように)使用し、さらに認証およびユーザー権利管理メカニズムを使用します(これにより、誰がイメージを探しているか、およびイメージが許可されているかどうかを確認できます)。
セキュリティにはコストがかかりますが、そのコンピューティングと保存の要件を許容したくない場合は、忘れてください。
ユーザーが簡単に迂回して完全な解像度の画像を取得したくない場合は、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
で指定してください。
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
あなたの質問では、それは純粋なPHPであり、データベースを使用してはならず、文字列をデコードする可能性があるはずです。そのため、ルールを少し曲げる必要があります。
「」
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よりもはるかに短いです。
申し訳ありませんが、既知の圧縮アルゴリズムよりもクエリ文字列を短くすることはできません。すでに述べたように、圧縮されたバージョンは、元のバージョンよりも数文字(約4〜6文字)短くなります。さらに、元の文字列は比較的簡単にデコードできます(たとえば、sha1やmd5のデコードとは異なります)。
Webサーバー構成を使用してURLを短くすることをお勧めします。イメージパスをIDに置き換えることで、さらに短くすることができます(データベースに(ID-filenameペアを格納))。
たとえば、次の Nginx 構成は/t/123456/700/500/4fc286f1a6a9ac4862bdd39a94a80858
のようなURLを受け入れます。ここで、
123456
)は、データベースからのイメージIDであると想定されています。700
および500
は画像の寸法です。# 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);
コメントセクションの議論から、あなたが本当に望んでいるのはオリジナルの高解像度画像を保護することです。
それを念頭に置いて、私は実際に最初にあなたのウェブサーバー設定(例えばApache mod_authz_core またはNginx ngx_http_access_module )を使用してウェブからディレクトリへのアクセスを拒否することを実際に行うことをお勧めします元の画像が保存されます。
サーバーはWebからの画像へのアクセスのみを拒否しますが、phpスクリプトから直接画像にアクセスできることに注意してください。 「リサイザー」スクリプトを使用して画像をすでに表示しているので、そこにハードリミットを設定し、それよりも大きいものに画像のサイズを変更することを拒否することをお勧めします(たとえば、$width = min(1000, $_GET['w'])
のようなもの)。
これで元の質問に答えられないことはわかっていますが、これは画像を保護するための適切な解決策になると思います。さらに、元の名前とサイズ変更パラメーターを難読化したい場合は、それを行うことができますが、背後にあるものを誰かが理解するかもしれないと心配することなく、適切と判断できます。
プレビュー画像が小さすぎると判断した場合にサイズを大きくしたいので、そこにサイズが必要だと言います-ここでの解決策は、画像サイズをphpスクリプトにハードコードし、URLから削除することです。将来的にサイズを変更する場合は、phpスクリプト(またはスクリプトに含めるconfig.php)のハードコードされた値を変更します。
また、画像データをname
、title
、description
などの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は使用しないでください)。
理論的には、短い入力文字セットと大きな出力文字セットが必要です。以下の例で説明します。 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回だけ)シャッフルできます。攻撃者にとっては少し難しいですが、実際には安全ではありません。とにかく、ユースケースには十分でしょう。
エンコーディングがセキュリティにどのように役立たないかについては多くのことが言われているので、私は短縮と美学に集中しています。
これを文字列と考えるのではなく、3つの個別のコンポーネントと考えることができます。次に、各コンポーネントのコードスペースを制限すると、物事をはるかに小さくまとめることができます。
例えば。
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" }
結果の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と同じようにラップします。結果はあなたのバージョンよりもさらに大きくなりますが、難読化を通して見るのがより難しくなり、そのため画像を不正なダウンロードから保護します。
短くするだけの場合、ファイル(またはそのフォルダー)の名前を変更せずに、またはデータベースを使用せずにそれを行う方法はありません。
提案されたファイルデータベースソリューションは、同時実行の問題を確実に引き起こします-同時にシステムを使用している人がいないか、非常に少ない場合を除きます。