لینک دانلود به صورت غیر مستقیم و غیر قابل حدس با php
لینک دانلود به صورت غیر مستقیم و غیر قابل حدس با php
به صورت ساده و معمولی ، برای ایجاد لینک دانلود یک فایل میتوان مسیر آن فایل ، که در سرور قرار دارد را استفاده کنیم . مانند مثال زیر:
<a href="https://zhupin.ir/i/wide-logo.png" >دانلود کنید</a>
در مثال بالا میبینید که در شناسه href در تگ A ، دقیقا مسیر فایل در سرور یا هاست را قرار دادم.
حال تصور کنیم نیاز داریم یک فایل فقط توسط کاربران عضو و یا یک کاربر خاص و یا بعد از خرید یک محصول و یا هر شرط دیگری قابل دانلود باشد. در این شرایط آیا مثال بالا راه درست و ایمنی هست؟
شاید بگویید اگر لازم باشد شرطی برقرار کنیم که فقط کاربران عضو و یا کاربر خاصی بتواند فایل را دانلود کند ، کافیست کد html که در مثال بالا ذکر کردیم را در شرط php که بیان میکند کاربر لاگین کرده یا نه قرار دهیم و از این طریق ، فقط کاربر عضو یا کاربر خاص میتواند آن را ببیند.
بله درست است ولی مشکل اینجاست که اگر به هر دلیل این لینک دست هر کسی بیافتد ، میتواند دانلود کند.
در ضمن همیشه برای امنیت ، لازم است مسیر فایل هایی که برای ما مهم هستند از دید کاربران مخفی بماند. به عنوان مثال ما سایتی داریم که مشتری بعد از خرید کالا میتواند فایل خود را دانلود کند. اگر مانند مثال بالا لینک سازی کنیم ، طبیعتا مسیر نگهداری فایل های فروشگاه ما مشخص است و به راحتی ، با یک پویشگر و یا indexer میتواند همه موارد داخل فولدر را دانلود کنید.
راه حل چیست؟؟؟؟
راه حل بسیار ساده است. کافیست از لینک غیر مستقیم استفاده کنیم. در php میتوان از force download استفاده کرد. نگران نباشید در ادامه کامل توضیح خواهیم داد.
تصور کنید محصولی داریم با عنوان "افزونه نظر دادن به محصولات ووکامرس" ، که مشتری پس از خرید ، لینک دانلود را دریافت میکند.
در اینجا ما باید لینک دانلود را طوری بسازیم که اولا خرید محصول توسط مشتری انجام شده باشد و این لینک برای آن مشتری منحصر به فرد باشد یعنی اگر مشتری دیگری همین محصول را خریداری کرد ، لینک متفاوتی خواهد داشت.
اولین چیزی که نیاز داریم دو تابع است. یک تابع برای هش کردن اختصاصی و یک تابع برای رمزگشایی همان هش که عمل encrypt و decrypt با یک کلید خاص انجام میشود که آن کلید را ما داریم. خب کار خیلی راحت شد . میتوانیم هرجور لینکی با این عمل هش یا encrypt بسازیم که اصلا مشخص نیست محتوای آن چه هست و سمت سرور یا در بخش backend (بک اند) هم این هش را معکوس یا decrypt میکنیم و میفهمیم چه چیزی درخواست شده است.
دو تابع زیر را برای رمزگذاری و رمزگشایی استفاده میکنیم:
<?php
function MyEncryption($string,$secret_key=''){
$encrypt_method = "AES-256-CBC";
$secret_iv = md5($secret_key);
$key = hash('sha256', $secret_key);
$iv = substr(hash('sha256', $secret_iv), 0, 16);
return (openssl_encrypt($string, $encrypt_method, $key, 0, $iv));
}
function MyDecryption($string,$secret_key=false){
$encrypt_method = "AES-256-CBC";
$secret_iv = md5($secret_key);
$key = hash('sha256', $secret_key);
$iv = substr(hash('sha256', $secret_iv), 0, 16);
return openssl_decrypt(($string), $encrypt_method, $key, 0, $iv);
}
?>
تابع MyEncription برای هش کردن یا encrypt کردن یا رمزگذاری و تابع MyDecription برای رمزگشایی یا decrypt استفاده میشود.
در هر دو تابع توجه کنید که آرگومان دوم کلید است که یک رشته دلخواه است.
در مثال زیر با کمک دو تابع بالا یک رشته را ابتدا هش میکنیم و سپس رمزگشایی می کنیم:
<?php
$MyString = "وب سایت ژوپین"
$MyKey = "idjkcks748jkdmkk6&#kdk8";
/***
مقدار دلخواه می باشد ولی هر چه برای رمزگذاری استفاده شود برای رمزگشایی هم باید همان کلید استفاده شود
**/
$Hashed = MyEncryption($MyString,$MyKey);
echo $Hashed ; /*یک رشته مبهم مشاهده خواهید کرد*/
echo '<hr/>';
$UnHashed = MyDeCryption($Hashed,$MyKey);
echo $UnHashed; /**همان مقدار اولیه یعنی وب سایت ژوپین را میبینید **/
?>
خب میریم سراغ لینک سازی که روش های مختلف و دلخواهی دارد . مثلا من من در آدرس لینک دانلود ، مقدار id آن محصول را همراه با Id کاربر میفرستم یا اصلا نیاز به ارسال id کاربر نیست چون در بک اند میتوان کاربر فعلی را چک کرد ، پس نیازی به id کاربر نداریم.
پس فقط id محصول را پاس میدهیم.
همانطور که گفتیم لینک دانلود باید غیر قابل حدس باشد. اگر id محصول را خیلی ساده به صورت عدد پاس دهیم ، خب به راحتی با تغییر عدد میتوان به فایل دیگر محصولات هم دسترسی داشت.
ابتدا id محصول را یک کلید منحصر به فرد هش یا کدگذاری میکنیم. مانند مثال زیر ، که من id کاربر را در عدد 14 ضرب و بعد با عدد 29 جمع کردم و کلید خودر را تولید میکنم. کاملا دلخواه.
<?php
$MemberId = 65; /**بستگی به سیستم شما دارد مصلا اگر وردپرس هستید از تابع get_use_id() میتوان بهره برد**/
$MyKey = (string)(($MemberId*14)+29); /**به حالت رشته تبدیل کردیم محض احتیاط**/
$PoductId = 130; /**این هم مثل آی دی کاربر بستگی به سیستم شما دارد که چگونه باید بیرون کشید**/
$HashedUrl = MyEncryption($PoductId,$MyKey);
?>
<a hef="<?php echo 'https://mysite.ir/download.php?file='.$HashedUrl ?>" >لینک دانلود محصول</a>
در مثال بالا id محصول را با کلیدی که قبلا توضیح دادیم هش کردیم و در کد html لینک ، به مسیر دانلود خود که در این مثال download.php هست با متد GET و مدار file پاس دادیم.
حال در فایل download.php یا هر مسیری که شما برای بخش php و بک اند دانلود دارید اقدامات زیر را انجام دهید:
<?php
$FileHashedString = isset($_GET['file']) ? trim($_GET['file']) : '';
if($FileHashedString==""){
echo 'No File';
exit;
}
$MemberId = 65; /***با توجه به سیستم سایت خود آی دی کاربر جاری را بگیرید***/
$MyKey = ($MemberId*14)+29;
$UnHashedFile = MyDecryption($FileHashedString,$MyKey);
/**
مقدار
$UnHashedFile
رمزگشایی شده است و طبق روال مثال قبل عدد
130
می باشد.
در این مرحله با داشتن آی دی محصول در دیتابیس محصول را جستجو و آن را میگیریم
نام فایل محصول هم در دیتابیس به عنوان مثال در ستونی با نام
attachment
می باشد.
همچنین چک میکنیم که کاربری که آی دی اونو داریم خرید از محصول داشته یا نه و اگر همه شروط درست بود ادامه میدهیم
**/
$q= $mysql->query('SELECT * FROM products WHERE id = '.(intval($ProductId)).' LIMIT 1 ');
$Product = $q->num_rows ? $q->fetch_object() : false;
if(!$Poduct){
die('No Poduct Founds');
}
/**
چک کردن خرید محصول توسط کاربر جاری
توجه کنید که کوئری های من فقط مثال است و شما طبق دیتابیس خود عمل کنید
**/
$q = $mysql->query('SELECT * FROM orders WHERE product_id='.intval($ProductId).' AND member_id='.intval($MemberId).' ');
if($q->num_rows ==0){
die('you Should Buy this product');
}
$file = $Product->attachment;
$name = basename($file);
$size = filesize($file);
set_time_limit(0);
$chunksize = 10 * (1024*1024);
header('Content-Type: '.filetype($file));
header('Content-Transfer-Encoding: binary');
header('Content-Length: '.$size);
header('Content-Disposition: attachment;filename="'.basename($name).'"');
readfile($file);
exit;
?>
ارسال نظر
جهت قرار دادن کد در متن کامنت ، متن مورد نظر را انتخاب کنید و دکمه بالا را بزنید