تحميل الملفات مع Turbogears 2
-
24-09-2019 - |
سؤال
لقد كنت أحاول تحديد طريقة "أفضل الممارسات" لإدارة عمليات تحميل الملفات باستخدام 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()
إذا افترضنا أنني أفهم بشكل صحيح ، هل سيتجنب شيء من هذا القبيل مشكلة "التسمية" الأساسية؟ id = uuid.
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()
المحلول
mhawke - أنت على صواب ، يجب عليك التعامل مع ذلك - يعتمد على ما تفعله مع الملف ، إذا لم يكن الأمر مهمًا إذا كان هناك تصادم اسم على سبيل المثال ، فأنت تهتم فقط بأحدث إصدار من بعض البيانات ، فمن المحتمل ألا يكون هناك لا المشكلة ، أو إذا لم يكن اسم الملف مهمًا في الواقع فقط محتويات الملف ، ولكنه لا يزال ممارسته السيئة.
يمكنك استخدام tempfile المسماة في TMP Dir ، ثم نقل الملف بمجرد التحقق من صحة إلى موقعه النهائي. أو يمكنك التحقق من اسم الملف غير موجود بالفعل مثل:
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 لترتيب اسم الملف ...
نصائح أخرى
أريد فقط أي شخص يأتي إلى هنا يبحث عن إجابات ، لمعرفة ذلك Allesandro Molinaمكتبة رائعة مستودع يشكل أفضل إجابة على هذا السؤال.
إنه يحل كل من مشكلات التسمية والنسخ ، وسوف يتضمن بشكل جيد في تطبيق Turbogears الخاص بك. يمكنك استخدامه مع 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'س GridFSStorage
, وأمازون S3Storage
. وعلى الأقل بالنسبة للملفات المخزنة محليًا وفي S3 ، fileid
سيتم توليدها بواسطة uuid.uuid1()
.
لا أعرف الكثير عن Turbogears وما إذا كان يمكن أن يوفر أي شيء لتجنب ما يلي ، لكن يبدو لي أن هذا الرمز محفوف بالخطر. قد يكون من الممكن للمستخدم الضار الكتابة فوق (أو إنشاء) أي ملف تربطه عملية Python Turbogears.
ماذا إذا asset_dirname
هو /tmp
, ، محتويات file.filename
هو ../../../../../../../etc/passwd
ومحتويات الملف root::0:0:root:/root:/bin/bash
؟ في بيئة UNIX ، سيفتح هذا الرمز (الأذونات المعلقة) الملف /tmp/../../../../../../../etc/passwd
في الوضع المقطوع ، ثم نسخ محتويات الملف الذي تم تحميله إليه - الكتابة فوق ملف كلمة مرور النظام بشكل فعال وتحديد مستخدم جذر بدون كلمة مرور. من المفترض أن هناك أشياء سيئة يمكن القيام بها لآلة Windows أيضًا.
حسنًا ، هذا مثال متطرف يتطلب أن يعمل Python كما root
(لا أحد يفعل ذلك ، أليس كذلك؟). حتى إذا كان Python يعمل كمستخدم منخفض الانتشار ، فقد يتم كتابة الملفات التي تم تحميلها مسبقًا في الإرادة.
لتلخيص ، لا تثق في إدخال المستخدم ، وفي هذه الحالة ، قدم المستخدم اسم الملف المتاح في file.filename
.
أليس Turbogears فقط أبراج مع إضافات؟ يمكنك التحقق من المساعدة هناك:
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())
لدى Werkzeug وظيفة مساعدة جيدة جدًا لتأمين أسماء الملفات المسمى Secure_filename. أعتقد أنه يمكنك تبنيه واستخدامه.
حول كيفية الذهاب ، سأقوم بإثارة الإجابات الجيدة التي تم تقديمها بالفعل.
إليكم 2 بنس الخاص بي حول تسمية الملف المخزن.
في الواقع ، قد يؤدي حفظ الملف باستخدام الاسم الأصلي إلى ضعف. الاستخدام الوحيد الذي أقوم به للاسم الأصلي ، إن وجد ، هو تلميح اكتشاف نوع التمرير.
على أي حال ، يجب إعطاء الملفات التي سيتم حفظها أسماء فريدة ، عن طريق هوية سجل أو شيء مشابه ، والاحتفاظ به في مكان تحت سيطرة مالك دليل التطبيق ، وهو مستخدم منتظم ، أو في خدمة تخزين أخرى ، مثل المذكورة أعلاه مستودع الخ.
إنها مسألة تصميم نظام جيد للنظام :).