2010-03-30 63 views
1

我使用以下代碼在django應用程序中從登錄受保護的視圖中提供上傳的文件。使用django提供服務文件 - 這是一個安全漏洞

您是否認爲此代碼中存在安全漏洞?我有點擔心用戶可能會在上傳/之後在url中放置任意字符串,並將其直接映射到本地文件系統。

其實我不認爲這是一個漏洞問題,因爲對文件系統的訪問僅限於使用UPLOAD_LOCATION設置定義的文件夾中的文件。

UPLOAD_LOCATION = is set to a not publicly available folder on the webserver 

url(r'^upload/(?P<file_url>[/,.,\s,_,\-,\w]+)', 'project_name.views.serve_upload_files', name='project_detail'), 

@login_required 
def serve_upload_files(request, file_url): 
    import os.path 
    import mimetypes 
    mimetypes.init() 

    try: 
     file_path = settings.UPLOAD_LOCATION + '/' + file_url 
     fsock = open(file_path,"r") 
     file_name = os.path.basename(file_path) 
     file_size = os.path.getsize(file_path) 
     print "file size is: " + str(file_size) 
     mime_type_guess = mimetypes.guess_type(file_name) 
     if mime_type_guess is not None: 
      response = HttpResponse(fsock, mimetype=mime_type_guess[0]) 
     response['Content-Disposition'] = 'attachment; filename=' + file_name 
     #response.write(file)    
    except IOError: 
     response = HttpResponseNotFound() 
    return response 

編輯:根據伊格納西奧巴斯克斯 - 艾布拉姆斯評論更新來源:

import os.path 
import mimetypes 

    @login_required 
    def serve_upload_files(request, file_url): 
    mimetypes.init() 
    try: 
     file_path = os.path.join(settings.UPLOAD_LOCATION, file_url) 
     #collapse possibly available up-level references 
     file_path = os.path.normpath(file_path) 
     #check if file path still begins with settings.UPLOAD_LOCATION, otherwise the user tampered around with up-level references in the url 
     #for example this input: http://127.0.0.1:8000/upload/..\test_upload.txt results having the user access to a folder one-level higher than the upload folder 
     #AND check if the common_prefix ends with a dir separator, Because although '/foo/barbaz' starts with '/foo/bar' 
     common_prefix = os.path.commonprefix([settings.UPLOAD_LOCATION, file_path]) 
     if common_prefix == settings.UPLOAD_LOCATION and common_prefix.endswith(os.sep): 
      fsock = open(file_path,"r") 
      file_name = os.path.basename(file_path) 
      mime_type_guess = mimetypes.guess_type(file_name) 
      if mime_type_guess is not None: 
       response = HttpResponse(fsock, mimetype=mime_type_guess[0]) 
       response['Content-Disposition'] = 'attachment; filename=' + file_name 
      else: 
       response = HttpResponseNotFound() 
     else: 
      print "wrong directory" 
      response = HttpResponseNotFound()   
    except IOError: 
     response = HttpResponseNotFound() 
    return response 

回答

5

一些提示:

  1. 使用os.path.join()加入路徑在一起。
  2. 使用os.path.normpath()獲取沒有「..」引用的實際路徑。
  3. 使用os.path.commonprefix()針對UPLOAD_LOCATION和生成的路徑,並驗證結果以UPLOAD_LOCATION開頭。
  4. 確保UPLOAD_LOCATION以dir分隔符結尾。

TL; DR:使用os.path

+0

謝謝,我會嘗試這些提示,並將粘貼新代碼在我的問題。爲什麼是Nr。 4重要? – 2010-03-31 07:56:02

+1

因爲雖然'/ foo/barbaz'以'/ foo/bar'開頭,但它不在它之下。 – 2010-03-31 08:09:22

+0

我重新編寫了源代碼。現在看起來很省錢。你怎麼看? – 2010-03-31 09:02:51