Django - 安全(九)

安全

1,CSRF攻擊

1,CSRF攻擊概述

CSRF(Cross Site Request Forgery, 跨站域請求僞造)是一種網絡的攻擊方式,它在 2007 年曾被列爲互聯網 20 大安全隱患之一。其餘安全隱患,好比 SQL 腳本注入,跨站域腳本攻擊等在近年來已經逐漸爲衆人熟知,不少網站也都針對他們進行了防護。然而,對於大多數人來講,CSRF 卻依然是一個陌生的概念。即使是大名鼎鼎的 Gmail, 在 2007 年末也存在着 CSRF 漏洞,從而被黑客攻擊而使 Gmail 的用戶形成巨大的損失。html

2,CSRF攻擊原理

網站是經過cookie來實現登陸功能的。而cookie只要存在瀏覽器中,那麼瀏覽器在訪問這個cookie的服務器的時候,就會自動的攜帶cookie信息到服務器上去。那麼這時候就存在一個漏洞了,若是你訪問了一個別有用心或病毒網站,這個網站能夠在網頁源代碼中插入js代碼,使用js代碼給其餘服務器發送請求(好比ICBC的轉帳請求)。那麼由於在發送請求的時候,瀏覽器會自動的把cookie發送給對應的服務器,這時候相應的服務器(好比ICBC網站),就不知道這個請求是僞造的,就被欺騙過去了。從而達到在用戶不知情的狀況下,給某個服務器發送了一個請求(好比轉帳)。python

3,防護CSRF攻擊

CSRF攻擊的要點就是在向服務器發送請求的時候,相應的cookie會自動的發送給對應的服務器。形成服務器不知道這個請求是用戶發起的仍是僞造的。這時候,咱們能夠在用戶每次訪問有表單的頁面的時候,在網頁源代碼中加一個隨機的字符串叫作csrf_token,在cookie中也加入一個相同值的csrf_token字符串。之後給服務器發送請求的時候,必須在body中以及cookie中都攜帶csrf_token,服務器只有檢測到cookie中的csrf_token和body中的csrf_token都相同,才認爲這個請求是正常的,不然就是僞造的。那麼黑客就沒辦法僞造請求了。在Django中,若是想要防護CSRF攻擊,應該作兩步工做。第一個是在settings.MIDDLEWARE中添加CsrfMiddleware中間件。第二個是在模版代碼中添加一個input標籤,加載csrf_token。示例代碼以下:git

 

服務器代碼:github

MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.middleware.gzip.GZipMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
]

模版代碼:ajax

<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}"/>

或者是直接使用csrf_token標籤,來自動生成一個帶有csrf token的input標籤:正則表達式

{% csrf_token %}

4,使用ajax處理csrf防護

若是用ajax來處理csrf防護,那麼須要手動的在form中添加csrfmiddlewaretoken,或者是在請求頭中添加X-CSRFToken。咱們能夠從返回的cookie中提取csrf token,再設置進去。示例代碼以下:sql

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;

5,iframe相關知識

1,iframe能夠加載嵌入別的域名下的網頁。也就是說能夠發送跨域請求。好比我能夠在我本身的網頁中加載百度的網站,示例代碼以下:數據庫

<iframe src="http://www.baidu.com/">
</ifrmae>

2,由於iframe加載的是別的域名下的網頁。根據同源策略,js只能操做屬於本域名下的代碼,所以js不能操做經過iframe加載來的DOM元素。
3,若是ifrmae的src屬性爲空,那麼就沒有同源策略的限制,這時候咱們就能夠操做iframe下面的代碼了。而且,若是src爲空,那麼咱們能夠在iframe中,給任何域名均可以發送請求。
4,直接在iframe中寫html代碼,瀏覽器是不會加載的。django

2,XSS攻擊

XSS(Cross Site Script)攻擊又叫作跨站腳本攻擊。他的原理是用戶在使用具備XSS漏洞的網站的時候,向這個網站提交一些惡意的代碼,當用戶在訪問這個網站的某個頁面的時候,這個惡意的代碼就會被執行,從而來破壞網頁的結構,獲取用戶的隱私信息等。跨域

1,XSS攻擊場景

好比A網站有一個發佈帖子的入口,若是用戶在提交數據的時候,提交了一段js代碼好比:<script>alert("hello world");</script>,而後A網站在渲染這個帖子的時候,直接把這個代碼渲染了,那麼這個代碼就會執行,會在瀏覽器的窗口中彈出一個模態對話框來顯示hello world!若是攻擊者能成功的運行以上這麼一段js代碼,那他能作的事情就有不少不少了!

2,XSS攻擊防護

1,若是不須要顯示一些富文本,那麼在渲染用戶提交的數據的時候,直接進行轉義就能夠了。

在Django的模板中默認就是轉義的。也能夠把數據在存儲到數據庫以前,就轉義再存儲進去,這樣之後在渲染的時候,即便不轉義也不會有安全問題,示例代碼以下:

from django.template.defaultfilters import escape
 from .models import Comment
 from django.http import HttpResponse
 def comment(request):
     content = request.POST.get("content")
     escaped_content = escape(content)
     Comment.objects.create(content=escaped_content)
     return HttpResponse('success')

2,若是對於用戶提交上來的數據包含了一些富文本(好比:給字體換色,字體加粗等),那麼這時候咱們在渲染的時候也要以富文本的形式進行渲染,也即須要使用safe過濾器將其標記爲安全的,這樣才能顯示出富文本樣式。可是這樣又會存在一個問題,若是用戶提交上來的數據存在攻擊的代碼呢,那將其標記爲安全的確定是有問題的。示例代碼以下:

# views.py
 def index(request):
     message = "<span style='color:red;'>紅色字體</span><script>alert('hello world');</script>";
     return render_template(request,'index.html',context={"message":message})

# index.html

那麼這時候該怎麼辦呢?這時候咱們能夠指定某些標籤咱們是須要的(好比:span標籤),而某些標籤咱們是不須要的(好比:script)那麼咱們在服務器處理數據的時候,就能夠將這些須要的標籤保留下來,把那些不須要的標籤進行轉義,或者乾脆移除掉,這樣就能夠解決咱們的問題了。這個方法是可行的,包括不少線上網站也是這樣作的,在Python中,有一個庫能夠專門用來處理這個事情,那就是sanitizer。接下來介紹這個庫的使用。

3,bleach庫

bleach庫是用來清理包含html格式字符串的庫。他能夠指定哪些標籤須要保留,哪些標籤是須要過濾掉的。也能夠指定標籤上哪些屬性是能夠保留,哪些屬性是不須要的。想要使用這個庫,能夠經過如下命令進行安裝:

pip install bleach

這個庫最重要的一個方法是bleach.clean方法,bleach.clean示例代碼以下:

import bleach
from bleach.sanitizer import ALLOWED_TAGS,ALLOWED_ATTRIBUTES

@require_http_methods(['POST'])
def message(request):
    # 從客戶端中獲取提交的數據
    content = request.POST.get('content')

    # 在默認的容許標籤中添加img標籤
    tags = ALLOWED_TAGS + ['img']
    # 在默認的容許屬性中添加src屬性
    attributes = {**ALLOWED_ATTRIBUTES,'img':['src']}

    # 對提交的數據進行過濾
    cleaned_content=bleach.clean

相關介紹以下:
1,tags:表示容許哪些標籤。
2,attributes:表示標籤中容許哪些屬性。
3,ALLOWED_TAGS:這個變量是bleach默認定義的一些標籤。若是不符合要求,能夠對其進行增長或者刪除。
4,ALLOWED_ATTRIBUTES:這個變量是bleach默認定義的一些屬性。若是不符合要求,能夠對其進行增長或者刪除。

bleach更多資料
github地址: https://github.com/mozilla/bleach
文檔地址: https://bleach.readthedocs.io/

3,點擊劫持攻擊

1,clickjacking攻擊

clickjacking攻擊又稱做點擊劫持攻擊。是一種在網頁中將惡意代碼等隱藏在看似無害的內容(如按鈕)之下,並誘使用戶點擊的手段。

2,clickjacking攻擊場景

場景一:
如用戶收到一封包含一段視頻的電子郵件,但其中的「播放」按鈕並不會真正播放視頻,而是鏈入一購物網站。這樣當用戶試圖「播放視頻」時,實際是被誘騙而進入了一個購物網站。
場景二:
用戶進入到一個網頁中,裏面包含了一個很是有誘惑力的按鈕A,可是這個按鈕上面浮了一個透明的iframe標籤,這個iframe標籤加載了另一個網頁,而且他將這個網頁的某個按鈕和原網頁中的按鈕A重合,因此你在點擊按鈕A的時候,實際上點的是經過iframe加載的另一個網頁的按鈕。好比我如今有一個百度貼吧,想要讓更多的用戶來關注,那麼咱們能夠準備如下一個頁面:

<!DOCTYPE html>
<html>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />
<head>
<title>點擊劫持</title>
<style>
    iframe{
        opacity:0.01;
        position:absolute;
        z-index:2;
        width: 100%;
        height: 100%;
    }
    button{
        position:absolute;
        top: 345px;

頁面看起來比較簡陋,可是實際上可能會比這些更精緻一些。當這個頁面經過某種手段被傳播出去後,用戶若是點擊了「查看詳情」,實際上點擊到的是關注的按鈕,這樣就能夠增長了一個粉絲。

3,clickjacking防護

像以上場景1,是沒有辦法避免的,受傷害的是用戶。而像場景2,受傷害的是百度貼吧網站和用戶。這種場景是能夠避免的,只要設置百度貼吧不容許使用iframe被加載到其餘網頁中,就能夠避免這種行爲了。咱們能夠經過在響應頭中設置X-Frame-Options來設置這種操做。X-Frame-Options能夠設置如下三個值:
1,DENY:不讓任何網頁使用iframe加載我這個頁面。
2,SAMEORIGIN:只容許在相同域名(也就是我本身的網站)下使用iframe加載我這個頁面。
3,ALLOW-FROM origin:容許任何網頁經過iframe加載我這個網頁。
在Django中,使用中間件django.middleware.clickjacking.XFrameOptionsMiddleware能夠幫咱們堵上這個漏洞,這個中間件設置了X-Frame-Option爲SAMEORIGIN,也就是隻有在本身的網站下才可使用iframe加載這個網頁,這樣就能夠避免其餘別有心機的網頁去經過iframe去加載了。

4,SQL注入

所謂SQL注入,就是經過把SQL命令插入到表單中或頁面請求的查詢字符串中,最終達到欺騙服務器執行惡意的SQL命令。具體來講,它是利用現有應用程序,將(惡意的)SQL命令注入到後臺數據庫引擎執行的能力,它能夠經過在Web表單中輸入(惡意)SQL語句獲得一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。 好比先前的不少影視網站泄露VIP會員密碼大多就是經過WEB表單遞交查詢字符暴出的。

1,場景

好比如今數據庫中有一個front_user表,表結構以下:

class User(models.Model):
    telephone = models.CharField(max_length=11)
    username = models.CharField(max_length=100)
    password = models.CharField(max_length=100)

而後咱們使用原生sql語句實現如下需求:

1,實現一個根據用戶id獲取用戶詳情的視圖。示例代碼以下:

def index(request):
         user_id = request.GET.get('user_id')
         cursor = connection.cursor()
         cursor.execute("select id,username from front_user where id=%s" % user_id)
         rows = cursor.fetchall()
         for row in rows:
             print(row)
         return HttpResponse('success')

這樣表面上看起來沒有問題。可是若是用戶傳的user_id是等於1 or 1=1,那麼以上拼接後的sql語句爲:

select id,username from front_user where id=1 or 1=1

以上sql語句的條件是id=1 or 1=1,只要id=1或者是1=1兩個有一個成立,那麼整個條件就成立。毫無疑問1=1是確定成立的。所以執行完以上sql語句後,會將front_user表中全部的數據都提取出來。

2,實現一個根據用戶的username提取用戶的視圖。示例代碼以下:

def index(request):
     username = request.GET.get('username')
     cursor = connection.cursor()
     cursor.execute("select id,username from front_user where username='%s'" % username)
     rows = cursor.fetchall()
     for row in rows:
         print(row)
     return HttpResponse('success')

這樣表面上看起來也沒有問題。可是若是用戶傳的username是zhiliao' or '1=1,那麼以上拼接後的sql語句爲:

select id,username from front_user where username='zhiliao' or '1=1'

以上sql語句的條件是username='zhiliao'或者是一個字符串,毫無疑問,字符串的判斷是確定成立的。所以會將front_user表中全部的數據都提取出來。

2,sql注入防護

以上即是sql注入的原理。他經過傳遞一些惡意的參數來破壞原有的sql語句以便達到本身的目的。固然sql注入遠遠沒有這麼簡單,咱們如今講到的只是冰山一角。那麼如何防護sql注入呢?歸類起來主要有如下幾點:
1,永遠不要信任用戶的輸入。對用戶的輸入進行校驗,能夠經過正則表達式,或限制長度;對單引號和 雙"-"進行轉換等。
2,永遠不要使用動態拼裝sql,可使用參數化的sql或者直接使用存儲過程進行數據查詢存取。好比:

def index(request):
     user_id = "1 or 1=1"
     cursor = connection.cursor()
     cursor.execute("select id,username from front_user where id=%s",(user_id,))
     rows = cursor.fetchall()
     for row in rows:
         print(row)
     return HttpResponse('success')

3,永遠不要使用管理員權限的數據庫鏈接,爲每一個應用使用單獨的權限有限的數據庫鏈接。
4,不要把機密信息直接存放,加密或者hash掉密碼和敏感的信息。
5,應用的異常信息應該給出儘量少的提示,最好使用自定義的錯誤信息對原始錯誤信息進行包裝。

3,在Django中如何防護sql注入

1,使用ORM來作數據的增刪改查。由於ORM使用的是參數化的形式執行sql語句的。
2,若是萬一要執行原生sql語句,那麼建議不要拼接sql,而是使用參數化的形式。

 

如何轉載,請在下方評論區或者私信聯繫博主 --- Qiuma

相關文章
相關標籤/搜索