मुझे लगा कि मैं एक संक्षिप्त (मेरे लिए यह छोटा है) "उत्तर" लिखूंगा ताकि मैं अपने बिंदुओं को संक्षेप में बता सकूं।
फ़ाइल संग्रहण प्रणाली बनाते समय कुछ "सर्वोत्तम अभ्यास"। फ़ाइल संग्रहण एक व्यापक श्रेणी है इसलिए इनमें से कुछ के लिए आपका माइलेज भिन्न हो सकता है। उन्हें वैसे ही लें जैसे मैंने जो पाया वह अच्छा काम करता है।
फ़ाइल नाम फ़ाइल को अंतिम उपयोगकर्ता द्वारा दिए गए नाम से संग्रहीत न करें। वे सभी प्रकार के भद्दे चरित्रों का उपयोग कर सकते हैं और करेंगे जो आपके जीवन को दयनीय बना देंगे। कुछ '
. जितने खराब हो सकते हैं सिंगल कोट्स, जो मूल रूप से लिनक्स पर बनाता है, इसलिए इसे पढ़ना असंभव है, या यहां तक कि फ़ाइल को हटाना भी असंभव है (सीधे)। कुछ चीजें आसान लग सकती हैं जैसे एक स्थान लेकिन आप इसका उपयोग कहां करते हैं और आपके सर्वर पर ओएस के आधार पर आप
one%20two.txt
के साथ वाइंड अप कर सकते हैं या one+two.txt
या one two.txt
जो आपके लिंक में सभी प्रकार की समस्याएं पैदा कर सकता है या नहीं भी कर सकता है।
करने के लिए सबसे अच्छी बात हैश बनाना है, कुछ इस तरह sha1
यह {user_id}{orgianl_name}
. जितना आसान हो सकता है उपयोगकर्ता नाम अन्य उपयोगकर्ता फ़ाइल नामों के साथ टकराव की संभावना कम करता है।
मैं file_hash('sha1', $contents)
करना पसंद करता हूं इस तरह अगर कोई एक ही फाइल को और अधिक अपलोड करता है तो एक बार जब आप उसे पकड़ सकते हैं (सामग्री वही है तो हैश वही है)। लेकिन अगर आप बड़ी फाइलों की उम्मीद करते हैं तो आप यह देखने के लिए कुछ बेंच मार्किंग करना चाहेंगे कि इसका प्रदर्शन किस प्रकार का है। मैं ज्यादातर छोटी फाइलों को संभालता हूं, इसलिए यह उसके लिए ठीक काम करता है।-नोट- कि टाइमस्टैम्प के साथ फाइल को अभी भी सहेजा जा सकता है क्योंकि पूरा नाम अलग है, लेकिन यह देखने में काफी आसान है, और इसे डेटाबेस में सत्यापित किया जा सकता है।
आप चाहे कुछ भी करें, मैं उसके पहले टाइमस्टैम्प time().'-'.$filename
लगाऊंगा . यह उपयोगी जानकारी है, क्योंकि यह फ़ाइल बनाने का पूर्ण समय है।
नाम के लिए एक उपयोगकर्ता फ़ाइल देता है। बस इसे डेटाबेस रिकॉर्ड में स्टोर करें। इस तरह आप उन्हें वह नाम दिखा सकते हैं जिसकी वे अपेक्षा करते हैं, लेकिन एक ऐसे नाम का उपयोग करें जिसे आप जानते हैं कि लिंक के लिए हमेशा सुरक्षित होता है।
$filename ='कुछ क्रैपी^ fileane.jpg';
$ext = strrchr($filename, '.');
echo "\nExt: {$ext}\n";
$hash = sha1('some crapy^ fileane.jpg');
echo "Hash: {$hash}\n";
$time = time();
echo "Timestamp: {$time}\n";
$hashname = $time.'-'.$hash.$ext;
echo "Hashname: $hashname\n";
आउटपुट
Ext: .jpg
Hash: bb9d2c2c7c73bb8248537a701870e35742b41c02
Timestamp: 1511853063
Hashname: 1511853063-bb9d2c2c7c73bb8248537a701870e35742b41c02.jpg
आप इसे यहां आज़मा सकते हैं
पथ फ़ाइल का पूरा पथ कभी भी संग्रहीत न करें। डेटाबेस में आपको केवल हैश नाम बनाने से हैश की आवश्यकता है। फ़ाइल में संग्रहीत फ़ोल्डर का "रूट" पथ PHP में किया जाना चाहिए। इसके कई फायदे हैं।
- निर्देशिका हस्तांतरण को रोकता है। क्योंकि आप अपने आस-पास के रास्ते के किसी भी हिस्से से नहीं गुजर रहे हैं, आपको किसी के
\..\..
के खिसकने के बारे में चिंता करने की ज़रूरत नहीं है। वहां और जाने वाले स्थानों में उन्हें नहीं करना चाहिए। इसका एक खराब उदाहरण यह होगा कि कोई व्यक्ति.htpassword
. को अधिलेखित कर दे नाम की एक फाइल अपलोड करके फाइल करें जिसमें डायरेक्टरी ट्रांसवर्स हो। - अधिक समान दिखने वाले लिंक, एक समान आकार, वर्णों का एक समान सेट है।
https://en.wikipedia.org/wiki/Directory_traversal_attack
- रखरखाव। रास्ते बदलते हैं, सर्वर बदलते हैं। आपके सिस्टम में बदलाव की मांग। यदि आपको उन फ़ाइलों को स्थानांतरित करने की आवश्यकता है, लेकिन आपने डीबी में उनके लिए पूर्ण पूर्ण पथ संग्रहीत किया है, तो आप
symlinks
के साथ सब कुछ एक साथ चिपका रहे हैं या अपने सभी रिकॉर्ड अपडेट कर रहे हैं।
इसके कुछ अपवाद हैं। यदि आप उन्हें मासिक फ़ोल्डर में या उपयोगकर्ता नाम से स्टोर करना चाहते हैं। आप पथ के उस हिस्से को एक अलग क्षेत्र में सहेज सकते हैं। लेकिन उस स्थिति में भी, आप इसे रिकॉर्ड में सहेजे गए डेटा के आधार पर गतिशील रूप से बना सकते हैं। मैंने पाया है कि जितना संभव हो उतना कम पथ जानकारी सहेजना सबसे अच्छा है। और वे एक कॉन्फ़िगरेशन या स्थिरांक बनाते हैं जिसका उपयोग आप उन सभी स्थानों पर कर सकते हैं जहां आपको फ़ाइल का पथ डालने की आवश्यकता होती है।
साथ ही path
और link
बहुत अलग हैं, इसलिए केवल नाम सहेज कर आप पथ से डेटा घटाए बिना किसी भी PHP पृष्ठ से इसे लिंक कर सकते हैं। मैंने हमेशा फ़ाइल नाम में जोड़ने के बाद पथ से घटाना आसान पाया है।
डेटाबेस (बस कुछ सुझाव, उपयोग भिन्न हो सकते हैं) हमेशा की तरह डेटा के साथ खुद से पूछें कि कौन, क्या, कहां, कब
- आईडी -
int
प्राथमिक कुंजी स्वतः वृद्धि - user_id -
int
विदेशी कुंजी, कौन इसे अपलोड किया - हैश -
char[40] *sha1*, unique
क्या हैश - हैशनाम -
varchar
{timestampl}-{हैश}।{ext} कहां हार्ड ड्राइव पर फाइलों का नाम - फ़ाइल नाम -
varchar
उपयोगकर्ता द्वारा दिया गया मूल नाम, इस तरह हम उन्हें वह नाम दिखा सकते हैं जिसकी वे अपेक्षा करते हैं (यदि यह महत्वपूर्ण है) - स्थिति -
enum[public,private,deleted,pending.. etc]
फ़ाइल की स्थिति, आपके उपयोग के मामले के आधार पर, आपको फ़ाइलों की समीक्षा करनी पड़ सकती है, या हो सकता है कि कुछ निजी हों, केवल उपयोगकर्ता उन्हें देख सकता है, शायद कुछ सार्वजनिक हैं आदि। - status_date -
timestamp|datetime
समय स्थिति बदल गई थी। - बनाएं_दिनांक -
timestamp|datetime
कब जिस समय फ़ाइल बनाई गई थी, एक टाइमस्टैम्प पसंद किया जाता है क्योंकि यह कुछ चीजों को आसान बनाता है लेकिन उस स्थिति में हैशनाम में उसी टाइमस्टैम्प का उपयोग होना चाहिए। - टाइप करें -
varchar
- माइम प्रकार, डाउनलोड करते समय माइम प्रकार सेट करने के लिए उपयोगी हो सकता है।
यदि आप अलग-अलग उपयोगकर्ताओं से एक ही फ़ाइल अपलोड करने की अपेक्षा करते हैं और आप file_hash
. का उपयोग करते हैं आप hash
बना सकते हैं user_id
. की संयुक्त अद्वितीय अनुक्रमणिका फ़ील्ड करें और hash
इस तरह यह केवल तभी विरोध करेगा जब एक ही उपयोगकर्ता ने एक ही फाइल अपलोड की हो। आप इसे अपनी आवश्यकताओं के आधार पर टाइमस्टैम्प और हैश के आधार पर भी कर सकते हैं।
यही वह बुनियादी सामान है जिसके बारे में मैं सोच सकता था, यह एक निरपेक्ष नहीं है बस कुछ क्षेत्र जो मैंने सोचा था कि उपयोगी होगा।
हैश को अपने आप रखना उपयोगी है, यदि आप इसे स्वयं स्टोर करते हैं तो आप इसे CHAR(40)
में स्टोर कर सकते हैं sha1 के लिए (डीबी में कम जगह लेता है तो VARCHAR
) और कोलेशन को UTF8_bin
. पर सेट करें जो बाइनरी है। यह इस पर खोजों को केस संवेदनशील बनाता है। हालांकि हैश टकराव की संभावना बहुत कम है, यह थोड़ा और सुरक्षा जोड़ता है क्योंकि हैश एक छोटे अक्षर के ऊपर होता है।
आप हमेशा hashname
बना सकते हैं फ्लाई पर यदि आप एक्सटेंशन स्टोर करते हैं, और टाइमस्टैम्प अलग होता है। यदि आप खुद को बार-बार चीजें बनाते हुए पाते हैं तो आप इसे PHP में काम को आसान बनाने के लिए बस इसे डीबी में स्टोर करना चाह सकते हैं।
मुझे लिंक में हैश डालना अच्छा लगता है, कोई एक्सटेंशन नहीं कुछ भी नहीं इसलिए मेरे लिंक इस तरह दिखते हैं।
http://www.example.com/download/ad87109bfff0765f4dd8cf4943b04d16a4070fea
वास्तविक सरल, वास्तविक सामान्य, यूआरएल में सुरक्षित हमेशा एक ही आकार आदि।
hashname
इसके लिए "फाइल" इस तरह होगी
1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea.jpg
यदि आपके पास एक ही फ़ाइल और भिन्न उपयोगकर्ता (जिसका मैंने ऊपर उल्लेख किया है) के साथ विरोध है। आप हमेशा टाइमस्टैम्प भाग को लिंक, user_id या दोनों में जोड़ सकते हैं। यदि आप user_id का उपयोग करते हैं, तो इसे शून्य के साथ बाएं पैड करना उपयोगी हो सकता है। उदाहरण के लिए कुछ उपयोगकर्ताओं के पास ID:1
. हो सकता है और कुछ ID:234
. हो सकते हैं ताकि आप इसे 4 स्थानों पर छोड़ सकें और उन्हें 0001
बना सकें और 0234
. फिर उसे हैश में जोड़ें, जो लगभग ध्यान देने योग्य नहीं है:
1511848005-ad87109bfff0765f4dd8cf4943b04d16a4070fea0234.jpg
यहाँ महत्वपूर्ण बात यह है कि क्योंकि sha1
हमेशा 40
होता है और आईडी हमेशा 4
होती है हम दोनों को सटीक और आसानी से अलग कर सकते हैं। और इस तरह, आप अभी भी इसे विशिष्ट रूप से देख सकते हैं। बहुत सारे अलग-अलग विकल्प हैं लेकिन बहुत कुछ आपकी आवश्यकताओं पर निर्भर करता है।
पहुंच जैसे डाउनलोड करना। आपको फ़ाइल को हमेशा PHP के साथ आउटपुट करना चाहिए, उन्हें फ़ाइल तक सीधे पहुंच न दें। सबसे अच्छा तरीका है कि फाइलों को वेबरूट के बाहर (public_html
के ऊपर संग्रहीत किया जाए) , या www
फ़ोल्डर)। फिर PHP में आप हेडर को सही प्रकार पर सेट कर सकते हैं और मूल रूप से फ़ाइल को पढ़ सकते हैं। यह वीडियो को छोड़कर लगभग हर चीज के लिए काम करता है। मैं वीडियो को हैंडल नहीं करता, इसलिए यह मेरे अनुभव से बाहर का विषय है। लेकिन मुझे इसके बारे में सोचना सबसे अच्छा लगता है क्योंकि सभी फ़ाइल डेटा टेक्स्ट है, इसके शीर्षलेख जो उस टेक्स्ट को एक छवि, या एक्सेल फ़ाइल या पीडीएफ में बनाते हैं।
उन्हें फ़ाइल तक सीधे पहुंच न देने का बड़ा फायदा यह है कि यदि आपके पास सदस्यता साइट है, तो आप अपनी सामग्री को बिना लॉगिन के एक्सेस नहीं करना चाहते हैं, तो आप आसानी से PHP में जांच सकते हैं कि क्या वे सामग्री देने से पहले लॉग इन हैं। और, चूंकि फ़ाइल वेबूट के बाहर है, वे इसे किसी अन्य तरीके से एक्सेस नहीं कर सकते।
सबसे महत्वपूर्ण बात यह है कि कुछ सुसंगत चुनें, जो अभी भी आपकी सभी जरूरतों को पूरा करने के लिए पर्याप्त लचीला हो।
मुझे यकीन है कि मैं और अधिक के साथ आ सकता हूं, लेकिन अगर आपके पास कोई सुझाव है तो बेझिझक टिप्पणी करें।
मूल प्रक्रिया प्रवाह
- उपयोगकर्ता फ़ॉर्म सबमिट करता है (
enctype="multipart/form-data"
)
https://www.w3schools.com/tags/att_form_enctype.asp
- सर्वर सुपर ग्लोबल्स
$_POST
. फॉर्म से पोस्ट प्राप्त करता है और$_FILES
http://php.net/manual/en/reserved.variables.files .php
$_FILES = [
'fieldname' => [
'name' => "MyFile.txt" // (comes from the browser, so treat as tainted)
'type' => "text/plain" // (not sure where it gets this from - assume the browser, so treat as tainted)
'tmp_name' => "/tmp/php/php1h4j1o" // (could be anywhere on your system, depending on your config settings, but the user has no control, so this isn't tainted)
'error' => "0" //UPLOAD_ERR_OK (= 0)
'size' => "123" // (the size in bytes)
]
];
-
त्रुटियों के लिए जाँच करें
if(!$_FILES['fielname']['error'])
-
प्रदर्शन नाम को स्वच्छ करें
$filename = htmlentities($str, ENT_NOQUOTES, "UTF-8");
-
फ़ाइल सहेजें, DB रिकॉर्ड बनाएं ( PSUDO-CODE )
इस तरह:
$path = __DIR__.'/uploads/'; //for exmaple
$time = time();
$hash = hash_file('sha1',$_FILES['fielname']['tmp_name']);
$type = $_FILES['fielname']['type'];
$hashname = $time.'-'.$hash.strrchr($_FILES['fielname']['name'], '.');
$status = 'pending';
if(!move_uploaded_file ($_FILES['fielname']['tmp_name'], $path.$hashname )){
//failed
//do somehing for errors.
die();
}
//store record in db
http://php.net/manual/en/function.move -uploaded-file.php
-
लिंक बनाएं (रूटिंग के आधार पर भिन्न होता है), सरल तरीका यह है कि आप अपने लिंक को इस तरह से करें
http://www.example.com/download?file={$hash}
लेकिनhttp://www.example.com/download/{$hash}
के बाद यह और भी खराब है -
उपयोगकर्ता क्लिक लिंक डाउनलोड पृष्ठ पर जाता है।
INPUT प्राप्त करें और रिकॉर्ड देखें
$hash = $_GET['file'];
$stmt = $PDO->prepare("SELECT * FROM attachments WHERE hash = :hash LIMIT 1");
$stmt->execute([":hash" => $hash]);
$row = $stmt->fetch(PDO::FETCH_ASSOC);
print_r($row);
http://php.net/manual/en/intro.pdo.php ए>
आदि....
चीयर्स!