文章目錄
- CSRF攻擊
-
- 概念
- 原理
- 防禦
- 使用ajax處理csrf防禦
- XSS攻擊
-
- 概念
- 場景
- 防禦
- bleach庫
- SQL注入
-
- 概念
- 場景
- 防禦
- Django中sql注入的防禦
CSRF攻擊
概念
CSRF
(Cross Site Request Forgery,
跨站域請求僞造
)是一種網絡的攻擊方式,它在 2007 年曾被列為網際網路 20 大安全隐患之一。其他安全隐患,比如 SQL 腳本注入,跨站域腳本攻擊等在近年來已經逐漸為衆人熟知,很多網站也都針對他們進行了防禦。然而,對于大多數人來說,CSRF 卻依然是一個陌生的概念。即便是大名鼎鼎的 Gmail, 在 2007 年底也存在着 CSRF 漏洞,進而被黑客攻擊而使 Gmail 的使用者造成巨大的損失。
原理
網站是通過
cookie
來實作登入功能的。而
cookie
隻要存在浏覽器中,那麼浏覽器在通路這個cookie的伺服器的時候,就會自動的攜帶cookie資訊到伺服器上去。那麼這時候就存在一個漏洞了,如果你通路了一個别有用心或病毒網站,這個網站可以在網頁源代碼中插入js代碼,使用js代碼給其他伺服器發送請求(比如ICBC的轉賬請求)。那麼因為在發送請求的時候,浏覽器會自動的把cookie發送給對應的伺服器,這時候相應的伺服器(比如ICBC網站),就不知道這個請求是僞造的,就被欺騙過去了。進而達到在使用者不知情的情況下,給某個伺服器發送了一個請求(比如轉賬)。
防禦
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
。示例代碼如下:
- 伺服器代碼
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',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware'
]
- 模闆代碼
或者是直接使用csrf_token标簽,來自動生成一個帶有csrf token的input标簽:
使用ajax處理csrf防禦
如果用
ajax
來處理
csrf
防禦,那麼需要手動的在form中添加
csrfmiddlewaretoken
,或者是在請求頭中添加
X-CSRFToken
。我們可以從傳回的
cookie
中提取
csrf token
,再設定進去。示例代碼如下:
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;
}
var myajax = {
'get': function (args) {
args['method'] = 'get';
this.ajax(args);
},
'post': function (args) {
args['method'] = 'post';
this._ajaxSetup();
this.ajax(args);
},
'ajax': function (args) {
$.ajax(args);
},
'_ajaxSetup': function () {
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
}
}
});
}
};
$(function () {
$("#submit").click(function (event) {
event.preventDefault();
var email = $("input[name='email']").val();
var money = $("input[name='money']").val();
myajax.post({
'url': '/transfer/',
'data':{
'email': email,
'money': money
},
'success': function (data) {
console.log(data);
},
'fail': function (error) {
console.log(error);
}
});
})
});
iframe相關知識:
- iframe可以加載嵌入别的域名下的網頁。也就是說可以發送跨域請求。比如我可以在我自己的網頁中加載百度的網站,示例代碼如下:
- 因為iframe加載的是别的域名下的網頁。根據同源政策,js隻能操作屬于本域名下的代碼,是以js不能操作通過iframe加載來的DOM元素。
- 如果ifrmae的src屬性為空,那麼就沒有同源政策的限制,這時候我們就可以操作iframe下面的代碼了。并且,如果src為空,那麼我們可以在iframe中,給任何域名都可以發送請求。
- 直接在iframe中寫html代碼,浏覽器是不會加載的。
XSS攻擊
概念
XSS
(Cross Site Script)攻擊又叫做
跨站腳本攻擊
。他的原理是使用者在使用具有XSS漏洞的網站的時候,向這個網站送出一些惡意的代碼,當使用者在通路這個網站的某個頁面的時候,這個惡意的代碼就會被執行,進而來破壞網頁的結構,擷取使用者的隐私資訊等。
場景
比如
A網站
有一個釋出文章的入口,如果使用者在送出資料的時候,送出一段
js
代碼:
<script>alert("hello world");</script>
,然後A網站在渲染這個文章的時候,直接把這個代碼渲染了,那麼這個代碼就會執行,會在浏覽器的視窗中彈出一個模态對話框來顯示hello world!如果攻擊者能成功的運作以上這麼一段js代碼,那他能做的事情就有很多很多了!
防禦
- 如果不需要顯示一些富文本,那麼在渲染使用者送出的資料的時候,直接進行轉義就可以了。在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')
- 如果對于使用者送出上來的資料包含了一些富文本(比如:給字型換色,字型加粗等),那麼這時候我們在渲染的時候也要以富文本的形式進行渲染,也即需要使用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})
那麼這時候該怎麼辦呢?這時候我們可以指定某些标簽我們是需要的(比如:span标簽),而某些标簽我們是不需要的(比如:script)那麼我們在伺服器處理資料的時候,就可以将這些需要的标簽保留下來,把那些不需要的标簽進行轉義,或者幹脆移除掉,這樣就可以解決我們的問題了。這個方法是可行的,包括很多線上網站也是這樣做的,在Python中,有一個庫可以專門用來處理這個事情,那就是sanitizer。接下來講下這個庫的使用。
bleach庫
bleach庫是用來清理包含html格式字元串的庫。他可以指定哪些标簽需要保留,哪些标簽是需要過濾掉的。也可以指定标簽上哪些屬性是可以保留,哪些屬性是不需要的。想要使用這個庫,可以通過以下指令進行安裝:
參考資料
github位址: https://github.com/mozilla/bleach
文檔位址: https://bleach.readthedocs.io/
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(content,tags=tags,attributes=attributes)
# 儲存到資料庫中
Message.objects.create(content=cleaned_content)
return redirect(reverse('index'))
相關介紹如下:
- tags:表示允許哪些标簽。
- attributes:表示标簽中允許哪些屬性。
- ALLOWED_TAGS:這個變量是bleach預設定義的一些标簽。如果不符合要求,可以對其進行增加或者删除。
- ALLOWED_ATTRIBUTES:這個變量是bleach預設定義的一些屬性。如果不符合要求,可以對其進行增加或者删除。
SQL注入
概念
所謂SQL注入,就是通過把SQL指令插入到表單中或頁面請求的查詢字元串中,最終達到欺騙伺服器執行惡意的SQL指令。具體來說,它是利用現有應用程式,将(惡意的)SQL指令注入到背景資料庫引擎執行的能力,它可以通過在Web表單中輸入(惡意)SQL語句得到一個存在安全漏洞的網站上的資料庫,而不是按照設計者意圖去執行SQL語句。 比如先前的很多影視網站洩露VIP會員密碼大多就是通過WEB表單遞交查詢字元暴出的。
場景
比如現在資料庫中有一個front_user表,表結構如下:
class User(models.Model):
telephone = models.CharField(max_length=11)
username = models.CharField(max_length=100)
password = models.CharField(max_length=100)
- 實作一個根據使用者id擷取使用者詳情的視圖。示例代碼如下:
這樣表面上看起來沒有問題。但是如果使用者傳的user_id是等于1 or 1=1,那麼以上拼接後的sql語句為: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')
以上sql語句的條件是id=1 or 1=1,隻要id=1或者是1=1兩個有一個成立,那麼整個條件就成立。毫無疑問1=1是肯定成立的。是以執行完以上sql語句後,會将front_user表中所有的資料都提取出來。select id,username from front_user where id=1 or 1=1
- 實作一個根據使用者的username提取使用者的視圖。示例代碼如下:
這樣表面上看起來也沒有問題。但是如果使用者傳的username是zhiliao’ or '1=1,那麼以上拼接後的sql語句為: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')
防禦
以上便是sql注入的原理。他通過傳遞一些惡意的參數來破壞原有的sql語句以便達到自己的目的。當然sql注入遠遠沒有這麼簡單,我們現在講到的隻是冰山一角。那麼如何防禦sql注入呢?歸類起來主要有以下幾點:
- 永遠不要信任使用者的輸入。對使用者的輸入進行校驗,可以通過正規表達式,或限制長度;對單引号和 雙"-"進行轉換等。
- 永遠不要使用動态拼裝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')
- 永遠不要使用管理者權限的資料庫連接配接,為每個應用使用單獨的權限有限的資料庫連接配接。
- 不要把機密資訊直接存放,加密或者hash掉密碼和敏感的資訊。
- 應用的異常資訊應該給出盡可能少的提示,最好使用自定義的錯誤資訊對原始錯誤資訊進行包裝。
Django中sql注入的防禦
在Django中如何防禦sql注入:
- 使用ORM來做資料的增删改查。因為ORM使用的是參數化的形式執行sql語句的。
- 如果萬一要執行原生sql語句,那麼建議不要拼接sql,而是使用參數化的形式。
#錯誤–不要直接格式化字元串
query = ‘SELECT * FROM myapp_person WHERE last_name = %s’ % lname
Person.objects.raw(query)
#正确–使用Django raw函數 功能進行安全轉義
name = ‘Doe’
Person.objects.raw(‘SELECT * FROM myapp_person WHERE last_name = %s’, [name])