2010-03-03 10 views
5

मैं Turbogears 2 के साथ फ़ाइल अपलोड प्रबंधित करने के लिए 'सर्वोत्तम प्रथाओं' तरीके से काम करने का प्रयास कर रहा हूं और इस प्रकार वास्तव में कोई उदाहरण नहीं मिला है। मैंने वास्तव में फ़ाइल अपलोड करने का एक तरीका निकाला है, लेकिन मुझे यकीन नहीं है कि यह हमें कितना विश्वसनीय है।Turbogears के साथ फ़ाइल अपलोड 2

इसके अलावा, अपलोड की गई फाइलों का नाम पाने का एक अच्छा तरीका क्या होगा?

file = request.POST['file'] 
    permanent_file = open(os.path.join(asset_dirname, 
     file.filename.lstrip(os.sep)), 'w') 
    shutil.copyfileobj(file.file, permanent_file) 
    file.file.close() 
    this_file = self.request.params["file"].filename 
    permanent_file.close() 

तो मान लीजिए कि मैं सही ढंग से समझ रहा हूं, क्या ऐसा कुछ मूल 'नामकरण' समस्या से बच जाएगा? आईडी = यूयूआईडी।

file = request.POST['file'] 
    permanent_file = open(os.path.join(asset_dirname, 
     id.lstrip(os.sep)), 'w') 
    shutil.copyfileobj(file.file, permanent_file) 
    file.file.close() 
    this_file = file.filename 
    permanent_file.close() 
+1

हां, 'uuid1()। हेक्स' या' uuid4()। हेक्स' नामकरण समस्या और अधिकांश सुरक्षा समस्याओं का समाधान करेगा। आपको uuid पर 'lstrip()' को कॉल करने की आवश्यकता नहीं है ('id' को एक चर नाम के रूप में उपयोग न करें - यह बिल्टिन आईडी()' मास्क करता है)। तो, एक अद्वितीय नाम उत्पन्न करने के लिए 'uuid' का उपयोग करें और अपलोड की गई डेटा को अपनी अपलोड निर्देशिका में उस नाम की फ़ाइल में कॉपी करें। यदि आपको उपयोगकर्ता द्वारा प्रदान की गई फ़ाइल नाम को स्टोर करने की आवश्यकता है, तो इसे अपने डेटाबेस में मेटा डेटा के रूप में सहेजें। ऐसे अतिरिक्त सुरक्षा मुद्दे हैं जिनका आप सामना करेंगे, जिनमें से कई का वर्णन यहां किया गया है: http://www.scanit.be/uploads/php-file-upload.pdf – mhawke

+0

ऐ, आपकी मदद के लिए धन्यवाद mhawke। महान पढ़ें। आउटटा जिज्ञासा, यूयूआईडी 4() का उपयोग करने के लिए कोई वास्तविक कारण है, केवल यूयूआईडी 4() पर हेक्स? पूर्व यूआरएल और इसी तरह के उपयोग के लिए थोड़ा अधिक मानव अनुकूल यूयूआईडी उत्पन्न करता है। (मैं दो यूयूआईडी उत्पन्न करने के बारे में सोच रहा हूं, एक डाउनलोड यूआरएल के लिए और वास्तविक फ़ाइल नाम/आईडी के लिए दूसरा। –

+0

फ़ाइल नाम की लंबाई के अलावा, 'uuid4()। हेक्स' बनाम' str (uuid4()) 'उतना ही कम है। मुझे यूआरएल और फाइलनाम के लिए स्वतंत्र यूयूआईडी रखने में कोई फायदा नहीं दिख रहा है क्योंकि आपको यूआरआईडी में वास्तविक फ़ाइल में यूयूआईडी को मैप करने के लिए अतिरिक्त परत की आवश्यकता होगी। – mhawke

उत्तर

1

@mhawke - आपको सही है कि आप इसे संभालना चाहते हैं - इस बात पर निर्भर करता है कि आप फ़ाइल के साथ क्या कर रहे हैं, अगर कोई फर्क नहीं पड़ता कि कोई नाम टक्कर है या नहीं, तो आप केवल कुछ डेटा के नवीनतम संस्करण की देखभाल करते हैं तो शायद कोई समस्या नहीं है, या यदि फ़ाइल नाम वास्तव में फ़ाइल सामग्री नहीं है, लेकिन यह अभी भी खराब अभ्यास है।

आप एक tmp dir में नामित tempfile का उपयोग कर सकते हैं, फिर फ़ाइल को अंतिम स्थान पर सत्यापित करने के बाद उसे स्थानांतरित करें। या फिर आप फ़ाइल नाम जाँचें पहले से ही ऐसा तरह अस्तित्व में नहीं है हो सकता है:

file.name = slugify(myfile.filename) 
name, ext = os.path.splitext(file.name) 
while os.path.exists(os.path.join(permanent_store, file.name)): 
    name += '_' 
    file.name = name + ext 

raw_file = os.path.join(permanent_store, file.name) 

slugify विधि फ़ाइल नाम को साफ़ रखने के लिए इस्तेमाल किया जाएगा ...

+1

अब हम दौड़ की स्थिति में शामिल हो रहे हैं।यह जांचने के बीच समय का एक अंतराल होगा कि फ़ाइल पहले से मौजूद है या वास्तव में इसे बना रही है या नहीं। क्या होगा यदि 2 उपयोगकर्ता एक ही समय में एक ही नाम की फ़ाइल अपलोड करते हैं? 'os.path.exists()' दोनों उदाहरणों में 'गलत' वापस कर सकता है, फिर जब फ़ाइलें बनाई जाती हैं, तो एक फ़ाइल दूसरे को ओवरराइट कर देगी। – mhawke

1

मैं TurboGears बारे में ज्यादा पता नहीं है और यह कुछ भी निम्नलिखित से बचने के लिए प्रदान कर सकते हैं कि क्या है, लेकिन मुझे लगता है कि इस कोड खतरे से भरा है। दुर्भावनापूर्ण उपयोगकर्ता को किसी भी फ़ाइल को ओवरराइट (या बनाने) के लिए संभव हो सकता है कि टर्बोगियर्स पायथन प्रक्रिया में पहुंच पहुंच हो।

क्या होगा अगर asset_dirname/tmp है, file.filename की सामग्री ../../../../../../../etc/passwd और फ़ाइल root::0:0:root:/root:/bin/bash की सामग्री है? यूनिक्स पर्यावरण में यह कोड (अनुमति लंबित) फ़ाइल को /tmp/../../../../../../../etc/passwd को ट्रंकेट मोड में खोल देगा और उसके बाद अपलोड की गई फ़ाइल की सामग्री को प्रतिलिपि बनाएँ - प्रभावी ढंग से आपके सिस्टम की पासवर्ड फ़ाइल को ओवरराइट कर रहा है और पासवर्ड के बिना रूट उपयोगकर्ता निर्दिष्ट कर रहा है। संभवतः ऐसी कई चीजें हैं जो विंडोज मशीन पर भी की जा सकती हैं।

ठीक है, यह एक चरम उदाहरण है जिसके लिए पाइथन root के रूप में चल रहा है (कोई भी ऐसा नहीं करता है, क्या वे करते हैं?)। यहां तक ​​कि यदि पाइथन कम-प्रबल उपयोगकर्ता के रूप में चल रहा है, तो पहले अपलोड की गई फ़ाइलों को इच्छानुसार ओवरराइट किया जा सकता है।

संक्षेप में, उपयोगकर्ता इनपुट पर भरोसा न करें, इस मामले में उपयोगकर्ता ने file.filename में उपलब्ध फ़ाइल नाम प्रदान किया है।

+0

ऐ, मुझे एहसास है कि यह एक जोखिम भरा प्रणाली है। मुझे वास्तव में यकीन नहीं है कि पाइथन आमतौर पर फ़ाइलों को कैसे संभालता है (एक PHP पृष्ठभूमि से आगे बढ़ रहा है, इसलिए यह एक कठिन बदलाव है, हालांकि मुझे पाइथन पसंद है।) यही कारण है कि मैं किसी ऐसे व्यक्ति की उम्मीद कर रहा हूं जिसने डिज़ाइन किया है बेहतर Turbogears अपलोड सिस्टम पता और मदद करेगा। और धन्यवाद, यह एक उपयोगी पहला कदम है। :) –

+0

मुझे लगता है कि टर्बोगर्स टॉस्का विजेट नामक उपयोगी नियंत्रणों का एक सेट प्रदान करता है और वह वें ere फ़ाइल अपलोड के लिए एक विजेट है। मैं उसमें ध्यान देने की सलाह दूंगा क्योंकि आप उम्मीद करेंगे कि मानक (wrt turbogears) के साथ कुछ खत्म हो जाएगा और उम्मीद है कि आपके उदाहरण में मैंने जो भेद्यताओं को इंगित किया है, उससे उम्मीद है। – mhawke

+1

उस विचार के लिए आपको बहुत बहुत धन्यवाद। ऐसा लगता है कि टोस्का विजेट्स में वास्तव में 'फ़ाइल फ़ील्ड' फ़ील्ड है। उनके दस्तावेज़ कुछ उलझन में हैं लेकिन मुझे अब तक यह मिल गया है। http://toscawidgets.org/documentation/tw.forms/modules/fields/basic_fields.html#filefield –

0

क्या अतिरिक्त के साथ केवल पिलोन नहीं है?

http://wiki.pylonshq.com/display/pylonsdocs/Form+Handling#file-uploads

हालांकि, यह अभी भी संभावित सुरक्षा दोष है कि उल्लेख किया mhawke शामिल हैं:: आप मदद वहाँ की जाँच कर सकता है इसके बाद के संस्करण के रूप में

os.path.join(permanent_store, myfile.filename.lstrip(os.sep)) 

एक ही वास्तव में अगर फ़ाइल नाम किसी भी तरह ../../../../../etc/passwd था तो आप बदल सकते उस फ़ाइल ...

तो आप बस वास्तविक फ़ाइल नाम तो तरह हो सकता है:

os.path.join(permanent_store, myfile.filename.split(os.sep).pop()) 
+0

'os.path.join (permanent_store, myfile.filename.split (os.sep) .pop() के साथ) ' उपयोगकर्ता द्वारा प्रदत्त डेटा पर भरोसा करने की समस्या अभी भी है। क्या होगा यदि उपयोगकर्ता ने उन फाइलों को अपलोड किया जिनके समान बेसनाम था? यदि बेसनाम टकराता है, तो पिछली बार अपलोड की गई फाइलें अभी भी ओवरराइट की जाएंगी। – mhawke

+0

तो क्या एक व्यावहारिक समाधान फाइलनाम से सभी आगे की स्लैश को पट्टी करना होगा? –

+0

वैसे पॉप सिर्फ फ़ाइल नाम का उपयोग करता है और पथ को अनदेखा करता है और आपके अस्थायी स्थान के बाहर पहुंचने में सुरक्षा दोष को रोकता है। – Ross

0

WERKZEUG फ़ाइल नाम हासिल करने के लिए एक बहुत अच्छा सहायक कार्य है secure_filename कहा जाता है।मुझे लगता है कि आप इसे अपनाने और उपयोग कर सकते हैं।

2

मैं सिर्फ यह चाहता हूं कि Allesandro Molina की महान लाइब्रेरी Depot इस प्रश्न का सबसे अच्छा जवाब है।

यह नामकरण और प्रतिलिपि बनाने दोनों मुद्दों को हल करता है, और यह आपके टर्बोगियर्स एप्लिकेशन में अच्छी तरह से शामिल होगा। आप इस उदाहरण में MongoDB GridFS साथ उपयोग कर सकते हैं,:

from depot.manager import DepotManager 

# Configure a *default* depot to store files on MongoDB GridFS 
DepotManager.configure('default', { 
    'depot.backend': 'depot.io.gridfs.GridFSStorage', 
    'depot.mongouri': 'mongodb://localhost/db' 
}) 

depot = DepotManager.get() 

# Save the file and get the fileid 
fileid = depot.create(open('/tmp/file.png')) 

# Get the file back 
stored_file = depot.get(fileid) 
print stored_file.filename 
print stored_file.content_type 

या आप आसानी से अपने SQLAlchemy मॉडल में लगाव क्षेत्रों बना सकते हैं जैसे:

from depot.fields.sqlalchemy import UploadedFileField 

class Document(Base): 
    __tablename__ = 'document' 

    uid = Column(Integer, autoincrement=True, primary_key=True) 
    name = Column(Unicode(16), unique=True) 

    content = Column(UploadedFileField) 

... और फिर, संलग्न फाइल के साथ दस्तावेजों भंडारण (स्रोत फ़ाइल या बाइट्स हो सकता है) हो जाता है के रूप में आसान के रूप में:

doc = Document(name=u'Foo', content=open('/tmp/document.xls')) 
DBSession.add(doc) 

डिपो दोनों LocalFileStorage का समर्थन करता है, MongoDB के +०१२३११९५७४५५, और अमेज़ॅन के S3Storage। और, कम से कम स्थानीय रूप से संग्रहीत फ़ाइलों के लिए और S3 में, fileiduuid.uuid1() द्वारा उत्पन्न किया जाएगा।

संबंधित मुद्दे