Python筆記:Django框架的靜態文件訪問、CSRF、狀態保持、中間件、密碼管理

靜態文件

  • 項目中的CSS、圖片、js都是靜態文件
  • 備註:本文基於django版本1.11展開, 技術棧會有所升級, 僅供參考!

1 ) 配置靜態文件

  • 在settings 文件中定義靜態內容

    STATIC_URL = '/static/'
    STATICFILES_DIRS = [
        os.path.join(BASE_DIR, 'static'),
    ]
    
  • 在項目根目錄下創建static目錄,再創建當前應用名稱的目錄

    • mysite/static/myapp/
  • 在模板中可以使用硬編碼

    • /static/my_app/myexample.jpg
  • 在模板中可以使用static編碼

    { % load static from staticfiles % }
    <img src="{ % static "app/myexample.jpg" % }" alt="My image"/>
    

Csrf

  • CSRF(Cross-site request forgery)跨站請求僞造,也被稱爲“One Click Attack”或者Session Riding,通常縮寫爲CSRF或者XSRF,是一種對網站的惡意利用。儘管聽起來像跨站腳本XSS,但它與XSS非常不同,XSS利用站點內的信任用戶,而CSRF則通過僞裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防範的資源也相當稀少)和難以防範,所以被認爲比XSS更具危險性

  • CSRF中間件和模板標籤爲防止跨站點請求僞造提供了易用的保護。

  • 當惡意網站包含鏈接,表單按鈕或某些旨在在您的網站上執行某些操作的JavaScript時,會使用在瀏覽器中訪問惡意網站的登錄用戶的憑據進行此類攻擊。

  • 還介紹了一種相關攻擊類型,即“登錄CSRF”,攻擊網站欺騙用戶的瀏覽器,以便使用其他人的憑證登錄到網站。

1 ) csrf的使用

  • 在django的模板中,提供了防止跨站攻擊的方法
    • 在settings.py中啓用’django.middleware.csrf.CsrfViewMiddleware’中間件,此項在創建項目時,默認被啓用
    • 在HTML的表單中添加標籤
      <form>
      { % csrf_token % }
      ...
      </form>
      

2 ) 取消保護

  • 如果某些視圖不需要保護,可以使用裝飾器csrf_exempt,模板中也不需要寫標籤,修改csrf2的視圖如下
    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt 
    def csrf2(request):
        uname=request.POST['uname'] 
        return render(request,'booktest/csrf2.html',{'uname':uname}) 
        運行上面的兩個請求,發現都可以請求
    

3 ) 保護原理

  • 加入標籤後,可以查看源代碼,發現多瞭如下代碼
    <input type='hidden' name='csrfmiddlewaretoken' value='nGjAB3Md9ZSb4NmG1sXDolPmh3bR2g59' />
    
  • 在瀏覽器的調試工具中,可以查看cookie信息
  • 查看跨站的信息,並沒有cookie信息,即使加入上面的隱藏域代碼,發現又可以訪問了
  • 結論:django的csrf不是完全的安全
  • 當提交請求時,中間件django.middleware.csrf.CsrfViewMiddleware會對提交的cookie及隱藏域的內容進行驗證,如果失敗則返回403錯誤

4 ) Ajax CSRF 認證

  • GET 請求不需要 CSRF 認證,POST 請求需要正確認證才能得到正確的返回結果。
  • 如果使用Ajax調用的時候,就要麻煩一些。需要注意以下幾點:
    • 在視圖中使用render (而不要使用 render_to_response)
    • 使用 jQuery 的 ajax 或者 post 之前 加入這個 js 代碼
      jQuery(document).ajaxSend(function(event, xhr, settings) {
          function getCookie(name) {
              var cookieValue = null;
              if (document.cookie && document.cookie != '') {
                  var cookies = document.cookie.split(';');
                  for (var i = 0; i < cookies.length; i++) {
                      var cookie = jQuery.trim(cookies[i]);
                      // Does this cookie string begin with the name we want?
                      if (cookie.substring(0, name.length + 1) == (name + '=')) {
                          cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                          break;
                      }
                  }
              }
              return cookieValue;
          }
          function sameOrigin(url) {
              // url could be relative or scheme relative or absolute
              var host = document.location.host; // host + port
              var protocol = document.location.protocol;
              var sr_origin = '//' + host;
              var origin = protocol + sr_origin;
              // Allow absolute or scheme relative URLs to same origin
              return (url == origin || url.slice(0, origin.length + 1) == origin + '/') ||
                  (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
                  // or any other URL that isn't scheme relative or absolute i.e relative.
                  !(/^(\/\/|http:|https:).*/.test(url));
          }
          function safeMethod(method) {
              return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
          }
      
          if (!safeMethod(settings.type) && sameOrigin(settings.url)) {
              xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
          }
      });
      
  • 或者 更爲優雅簡潔的代碼(不能寫在 .js 中,要直接寫在模板文件中):
    $.ajaxSetup({
        data: {csrfmiddlewaretoken: '{ { csrf_token } }' },
    });
    
  • 這樣之後,就可以像原來一樣的使用 jQuery.ajax() 和 jQuery.post()了

狀態保持

  • HTTP協議是無狀態的:每次請求都是一次新的請求,不會記得之前通信的狀態
  • 客戶端與服務器端的一次通信,就是一次會話
  • 實現狀態保持的方式:在客戶端或服務器端存儲與會話有關的數據
  • 存儲方式包括cookie、session,會話一般指session對象
  • 使用cookie,所有數據存儲在客戶端,注意不要存儲敏感信息
  • 推薦使用sesison方式,所有數據存儲在服務器端,在客戶端cookie中存儲session_id
  • 狀態保持的目的是在一段時間內跟蹤請求者的狀態,可以實現跨頁面訪問當前請求者的數據
  • 注意:不同的請求者之間不會共享這個數據,與請求者一一對應

1 ) 開啓session

  • 使用django-admin startproject創建的項目默認啓用
  • 禁用會話:刪除下面指定的兩個值,禁用會話將節省一些性能消耗
  • Django 中session需要依賴數據庫,因此需要確認數據庫中是否存在 與session相關的 表
  • 在settings.py文件中
    • INSTALLED_APPS列表中添加:‘django.contrib.sessions’
    • MIDDLEWARE_CLASSES列表中添加:‘django.contrib.sessions.middleware.SessionMiddleware’

2 ) 使用session

  • 啓用會話後,每個HttpRequest對象將具有一個session屬性,它是一個類字典對象
  • get(key, default=None):根據鍵獲取會話的值
  • clear():清除所有會話
  • flush():刪除當前的會話數據並刪除會話的Cookie
  • del request.session[‘member_id’]:刪除會話
  • 示例:
    #session設置
    request.session[key] = value
    
    #session獲取
    request.session.get(key,default=Node)
    
    #session刪除
    
    # 刪除單個key 不存在時報錯
    del request.session['a'] 
    
    #清除所有會話,但不會刪除數據
    request.session.clear() 
    
    #刪除當前的會話數據
    request.session.flush()
    

中間件

  • 中間件是一個輕量級、底層的插件系統,可以介入Django的請求和響應處理過程,修改Django的輸入或輸出
  • 激活:添加到Django配置文件中的MIDDLEWARE_CLASSES元組中
  • 使用中間件,可以干擾整個處理過程,每次請求中都會執行中間件的這個方法

1 ) 驗證用戶是否登陸示例

  • 在應用中創建AdminLoginMiddleware.py文件
    class AppMiddleware:
        def __init__(self, get_response):
            self.get_response = get_response
            print('middleware init!')
    
        def __call__(self, request):
            print('middleware call!')
            response = self.get_response(request)
            return response
    

2 ) 配置中間件

  • 在settings.py文件中修改MIDDLEWARE_CLASSES選項
    MIDDLEWARE = [
        #自定義的中間件
        'app.app_middleware.AppMiddleware'
    ]
    

使用Django中提供的密碼方案

  • 該django.contrib.auth.hashers模塊提供了一組函數來創建和驗證散列密碼。您可以獨立於User模型使用它們。
    # from django.contrib.auth.hashers import make_password, check_password
    # 對密碼進行加密操作
    # upass = make_password(request.POST['password'], None, 'pbkdf2_sha256')
    # print(upass) # 這裏是加密後的密碼
    # b = check_password('1234567','pbkdf2_sha256$36000$197nqXM6yxYv$Zxh/Vsns8qszkvUmY81BgrjCeLPXhCHLEilP6VO+Rnc=')
    # print(b) # False 備註:check_password的參數:第一個是加密前的數據, 第二個是加密後的數據
    
    # python中的MD5加密
    # 獲取密碼並md5
    import hashlib
    from django.http  import HttpResponse
    
    m = hashlib.md5()
    m.update(bytes(request.POST['password'], encoding="utf8"))
    str = m.hexdigest()
    return HttpResponse("123456的md5是:" + str)
    

相關代碼示例

  • 城市聯動
  • 文件上傳
  • 分頁
  • 富文本編輯器
  • session狀態維持
  • 中間件
  • 密碼管理
  • 示例詳見代碼倉庫
發佈了410 篇原創文章 · 獲贊 221 · 訪問量 69萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章