2009-10-05 61 views
3

我有這樣定義的模型的一部分:的Django的ImageField /自定義的FileField功能upload_to和安全

logo_image = models.ImageField(upload_to=lambda i, fn: "logo_%s"%(fn), height_field="logo_image_height", width_field="logo_image_width") 

,有一個關於upload_to功能問題。

根據django's documentation for FileField.upload_to,第二參數filename是「最初給予該文件的文件名」。

現在,瞭解HTTP,文件上傳等,最終用戶的客戶端可以很容易僞造文件名。特別是,最終客戶端不能上傳一個名爲「/ etc/passwd」的文件,例如,如果我使用我的天真代碼(lambda i, fn: "logo_%s"%(fn)),是不是將結果文件上傳到/etc/passwd?我需要跳過filename參數嗎?

#using django's example of using full paths in settings module, 
#MEDIA_ROOT="/tmp/media" 
>>> os.path.join("/tmp/media/", "apple.jpg") 
'/tmp/media/apple.jpg' 
>>> os.path.join("/tmp/media/", "/etc/passwd") 
'/etc/passwd' 

感謝您的任何建議/答案/澄清。

編輯

的重要的是尋找方法在有in files.py, near line 272

272   def get_directory_name(self): 
273    return os.path.normpath(force_unicode(datetime.datetime.now().strftime(smart_str(self.upload_to)))) 
274  
275   def get_filename(self, filename): 
276    return os.path.normpath(self.storage.get_valid_name(os.path.basename(filename))) 
277  
278   def generate_filename(self, instance, filename): 
279    return os.path.join(self.get_directory_name(), self.get_filename(filename)) 

定義自定義upload_to取代generate_filename()所看到here

226    if callable(upload_to): 
227     self.generate_filename = upload_to 

然後,在save() method

89  def save(self, name, content, save=True): 
90   name = self.field.generate_filename(self.instance, name) 
91   self.name = self.storage.save(name, content) 

將返回的文件名傳遞給存儲類,最終在_os.py util模塊safe_join中調用django替換函數。

這個功能似乎減輕了我的恐懼:

24 def safe_join(base, *paths): 
25  """ 
26  Joins one or more path components to the base path component intelligently. 
27  Returns a normalized, absolute version of the final path. 
28 
29  The final path must be located inside of the base path component (otherwise 
30  a ValueError is raised). 
31  """ 
+0

所以...你真的不需要那麼這個問題的回答? – Powerlord 2009-10-05 19:29:02

+0

對不起:)不出現。只是在這一點上尋找驗證(有人做另一個看看)。 – 2009-10-05 20:17:04

+1

如果您「回答自己的問題」,可能會更清楚,然後將其標記爲已回答,以便它不會留在未答覆的列表中。 – 2009-10-20 15:06:30

回答

1

我想你已經回答了你自己的問題。有一點需要澄清,os.path.join()的工作方式是去掉前面的目錄(根據與os.path相關的Python文檔)。因此,您在調用os.path.join()時觀察到的行爲與它的描述是一致的。

另一件需要注意的事情:get_filename()函數調用os.path.basename(),它將去除任何目錄路徑並僅返回基本名稱。因此,如果沒有upload_to =參數,就沒有這種可能性。但是,如果用自己的upload_to函數覆蓋ImageField(),則不會調用此函數,並且調用os.path.basename()可能會更好。首先,它將避免將文件名保存爲完整的目錄路徑。我發現它最好在我的upload_to函數中調用os.path.basename()。其他人是否已經陷入這個問題?

有關詳細信息,請參閱:http://hustoknow.blogspot.com/2010/08/try-me-out.html