原理分析:XSS(Cross Site Script)攻擊又叫做跨站腳本攻擊,是為不和層疊樣式表(Cascading Style Sheets, CSS)的縮寫(xiě)混淆,故將跨站腳本攻擊縮寫(xiě)為XSS。他的原理是用戶在使用具有XSS漏洞的網(wǎng)站的時(shí)候,向這個(gè)網(wǎng)站提交一些惡意的代碼,當(dāng)用戶在訪問(wèn)這個(gè)網(wǎng)站的某個(gè)頁(yè)面的時(shí)候,這個(gè)惡意的代碼就會(huì)被執(zhí)行,從而來(lái)破壞網(wǎng)頁(yè)的結(jié)構(gòu),獲取用戶的隱私信息等。
攻擊場(chǎng)景:比如A網(wǎng)站有一個(gè)發(fā)布帖子的入口,如果用戶在提交數(shù)據(jù)的時(shí)候,提交了一段js代碼比如:<script>alert("hello world");</script>,然后A網(wǎng)站在渲染這個(gè)帖子的時(shí)候,直接把這個(gè)代碼渲染了,那么這個(gè)代碼就會(huì)執(zhí)行,會(huì)在瀏覽器的窗口中彈出一個(gè)模態(tài)對(duì)話框來(lái)顯示hello world!如果攻擊者能成功的運(yùn)行以上這么一段js代碼,那他能做的事情就有很多很多了!
XSS攻擊的危害包括:
1、盜取各類用戶帳號(hào),如機(jī)器登錄帳號(hào)、用戶網(wǎng)銀帳號(hào)、各類管理員帳號(hào)
2、控制企業(yè)數(shù)據(jù),包括讀取、篡改、添加、刪除企業(yè)敏感數(shù)據(jù)的能力
3、盜竊企業(yè)重要的具有商業(yè)價(jià)值的資料
4、非法轉(zhuǎn)賬
5、強(qiáng)制發(fā)送電子郵件
6、網(wǎng)站掛馬
7、控制受害者機(jī)器向其它網(wǎng)站發(fā)起攻擊
XSS漏洞的分類
XSS漏洞按照攻擊利用手法的不同,有以下三種類型:
類型A,本地利用漏洞,這種漏洞存在于頁(yè)面中客戶端腳本自身。其攻擊過(guò)程如下所示:
Alice給Bob發(fā)送一個(gè)惡意構(gòu)造了Web的。
Bob點(diǎn)擊并查看了這個(gè)URL。
惡意頁(yè)面中的JavaScript打開(kāi)一個(gè)具有漏洞的HTML頁(yè)面并將其安裝在Bob電腦上。
具有漏洞的HTML頁(yè)面包含了在Bob電腦本地域執(zhí)行的JavaScript。
Alice的惡意腳本可以在Bob的電腦上執(zhí)行Bob所持有的權(quán)限下的命令。
類型B,反射式漏洞,這種漏洞和類型A有些類似,不同的是Web客戶端使用Server端腳本生成頁(yè)面為用戶提供數(shù)據(jù)時(shí),如果未經(jīng)驗(yàn)證的用戶數(shù)據(jù)被包含在頁(yè)面中而未經(jīng)HTML實(shí)體編碼,客戶端代碼便能夠注入到動(dòng)態(tài)頁(yè)面中。其攻擊過(guò)程如下:
Alice經(jīng)常瀏覽某個(gè)網(wǎng)站,此網(wǎng)站為Bob所擁有。Bob的站點(diǎn)運(yùn)行Alice使用用戶名/密碼進(jìn)行登錄,并存儲(chǔ)敏感信息(比如銀行帳戶信息)。
Charly發(fā)現(xiàn)Bob的站點(diǎn)包含反射性的XSS漏洞。
Charly編寫(xiě)一個(gè)利用漏洞的URL,并將其冒充為來(lái)自Bob的郵件發(fā)送給Alice。
Alice在登錄到Bob的站點(diǎn)后,瀏覽Charly提供的URL。
嵌入到URL中的惡意腳本在Alice的瀏覽器中執(zhí)行,就像它直接來(lái)自Bob的服務(wù)器一樣。此腳本盜竊敏感信息(授權(quán)、信用卡、帳號(hào)信息等)然后在Alice完全不知情的情況下將這些信息發(fā)送到Charly的Web站點(diǎn)。
類型C,存儲(chǔ)式漏洞,該類型是應(yīng)用最為廣泛而且有可能影響到Web服務(wù)器自身安全的漏洞,駭客將攻擊腳本上傳到Web服務(wù)器上,使得所有訪問(wèn)該頁(yè)面的用戶都面臨信息泄漏的可能,其中也包括了Web服務(wù)器的管理員。其攻擊過(guò)程如下:
Bob擁有一個(gè)Web站點(diǎn),該站點(diǎn)允許用戶發(fā)布信息/瀏覽已發(fā)布的信息。
Charly注意到Bob的站點(diǎn)具有類型C的XSS漏洞。
Charly發(fā)布一個(gè)熱點(diǎn)信息,吸引其它用戶紛紛閱讀。
Bob或者是任何的其他人如Alice瀏覽該信息,其會(huì)話cookies或者其它信息將被Charly盜走。
類型總結(jié):類型A直接威脅用戶個(gè)體,而類型B和類型C所威脅的對(duì)象都是企業(yè)級(jí)Web應(yīng)用。
django下的小例子模擬xss攻擊中的類型B:
廢話不多說(shuō),直接上代碼
models.py:
from django.db import models
# Create your models here.
class School(models.Model):
name = models.CharField('名字',max_length=50)
address = models.CharField('地址',max_length=100)
content = models.TextField('描述內(nèi)容',max_length=50,default='')
def __str__(self):
return self.name
class Meta:
verbose_name='學(xué)校'
verbose_name_plural=verbose_name
class Person(models.Model):
username=models.CharField('用戶名',max_length=20)
age = models.IntegerField('年齡')
school = models.ForeignKey(School,on_delete=models.CASCADE,verbose_name='學(xué)校')
def __str__(self):
return self.username
class Meta:
verbose_name='人物'
verbose_name_plural=verbose_name
myapp/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('index/', views.index,name='comment'),
]
myapp/views.py
from django.shortcuts import render,redirect,reverse
from .models import School
def index(request):
print(request.method)
if request.method=='GET':
context = {
'comments': School.objects.all()
}
return render(request, 'myapp/index.html', context=context)
if request.method == 'POST':
content = request.POST.get('content')
School.objects.create(content=content)
return redirect(reverse('comment'))
myapp/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>xss index page</h1>
<ul>
{% for comment in comments %}
<li>{{ comment.content|safe }}</li>{# 這里必須設(shè)置safe去掉django的防御 #}
{% endfor %}
</ul>
<form action="{% url 'comment' %}" method="post">
{% csrf_token %}
<textarea name="content" id="" cols="30" rows="3"></textarea>
<p>
<input type="submit" value="提交">
</p>
</form>
</body>
</html>
執(zhí)行后如下圖:
image.png
此時(shí)我往輸入框中輸入script代碼:<script>alert("hello world");</script>
點(diǎn)擊提交后結(jié)果如圖:
image.png
點(diǎn)擊確定后:
-2.png
此時(shí)數(shù)據(jù)庫(kù)中:
-1.png
如果此時(shí)我再往輸入框中輸入script代碼:<script> window.onload = function () { var imgTag = document.createElement("img"); imgTag.setAttribute('src','http://img.haote.com/upload/news/image/20170605/20170605144101_12960.jpg'); document.body.appendChild(imgTag); } </script>
同樣提交后:image.png
xss攻擊防御原理:我們只要轉(zhuǎn)義掉前段傳送過(guò)來(lái)的html標(biāo)簽即可,比如大于小于號(hào),或者判斷script字符是否存在,注意,這里不建議判斷,因?yàn)殡S便加幾個(gè)tab或者空格即可讓你的判斷失效!所以轉(zhuǎn)義script前后的大于小于號(hào)才是重點(diǎn),利用django很容易做到這件事,比如在html中去掉加入safe標(biāo)簽(默認(rèn)情況下django是不加safe的,也就是會(huì)默認(rèn)過(guò)濾轉(zhuǎn)義掉幾乎一切的違規(guī)字符),當(dāng)然你也可以直接在view中利用django.template.defaultfilters.escape()方法,來(lái)直接對(duì)傳送進(jìn)來(lái)的字符串進(jìn)行手動(dòng)轉(zhuǎn)義,這樣當(dāng)存進(jìn)去數(shù)據(jù)庫(kù)的時(shí)候就不會(huì)有"<",">",取而代之的是“<”和“>”,其他字符同理。。。下面給出了escape()默認(rèn)轉(zhuǎn)義的字符,它是django內(nèi)定的:
_html_escapes = {
ord('&'): '&',
ord('<'): '<',
ord('>'): '>',
ord('"'): '"',
ord("'"): ''',
}
下面給出了index.html代碼來(lái)解釋escape()用法:
from django.shortcuts import render,redirect,reverse
from .models import School
from django.template.defaultfilters import escape #導(dǎo)入escape
def index(request):
print(request.method)
if request.method=='GET':
context = {
'comments': School.objects.all()
}
return render(request, 'myapp/index.html', context=context)
if request.method == 'POST':
content = request.POST.get('content')
content=escape(content) #也加了這句
School.objects.create(content=content)
return redirect(reverse('comment'))
再次輸入同樣的script代碼后的數(shù)據(jù)庫(kù):0.png
如果你對(duì)django轉(zhuǎn)義需要研究,我在網(wǎng)上找到這篇文章適合你,但是有些地方不妥,有空我會(huì)重新整理這篇文章進(jìn)行更正!但是錯(cuò)誤的也就一兩處不傷大雅!
DJANGO基礎(chǔ)學(xué)習(xí)之轉(zhuǎn)義總結(jié):escape,autoescape,safe,mark_safe
但是問(wèn)題是:有時(shí)候我們需要允許一些html字符輸出,而不是禁止掉一切字符,此時(shí)我們需要借助bleach庫(kù)
bleach庫(kù):
bleach庫(kù)是用來(lái)清理包含html格式字符串的庫(kù)。他可以指定哪些標(biāo)簽需要保留,哪些標(biāo)簽是需要過(guò)濾掉的。也可以指定標(biāo)簽上哪些屬性是可以保留,哪些屬性是不需要的。想要使用這個(gè)庫(kù),可以通過(guò)以下命令進(jìn)行安裝:
pip install bleach或者pip3 install bleach
這個(gè)庫(kù)最重要的一個(gè)方法是bleach.clean方法,bleach.clean示例代碼如下:
import bleach
from bleach.sanitizer import ALLOWED_TAGS,ALLOWED_ATTRIBUTES
@require_http_methods(['POST'])
def message(request):
# 從客戶端中獲取提交的數(shù)據(jù)
content = request.POST.get('content')
# 在默認(rèn)的允許標(biāo)簽中添加img標(biāo)簽
tags = ALLOWED_TAGS + ['img']
# 在默認(rèn)的允許屬性中添加src屬性
attributes = {**ALLOWED_ATTRIBUTES,'img':['src']}
# 對(duì)提交的數(shù)據(jù)進(jìn)行過(guò)濾
cleaned_content=bleach.clean(content,tags=tags,attributes=attributes)
# 保存到數(shù)據(jù)庫(kù)中
Message.objects.create(content=cleaned_content)
return redirect(reverse('index'))
相關(guān)介紹如下:
-tags:表示允許哪些標(biāo)簽。
-attributes:表示標(biāo)簽中允許哪些屬性。
-ALLOWED_TAGS:這個(gè)變量是bleach默認(rèn)定義的一些標(biāo)簽。如果不符合要求,可以對(duì)其進(jìn)行增加或者刪除。有默認(rèn)值,可選參數(shù)。
-ALLOWED_ATTRIBUTES:這個(gè)變量是bleach默認(rèn)定義的一些屬性。如果不符合要求,可以對(duì)其進(jìn)行增加或者刪除。有默認(rèn)值,可選參數(shù)。
bleach更多資料:
tags默認(rèn)允許的標(biāo)簽:

attributes默認(rèn)允許的屬性:
- github地址: https://github.com/mozilla/bleach
- 文檔地址: https://bleach.readthedocs.io/
在上面我們舉的小例子中views.py代碼只要這樣寫(xiě):
from django.shortcuts import render,redirect,reverse
from .models import School
import bleach
from bleach.sanitizer import ALLOWED_TAGS,ALLOWED_ATTRIBUTES
from django.template.defaultfilters import escape
# Create your views here.
def index(request):
print(request.method)
if request.method=='GET':
context = {
'comments': School.objects.all()
}
return render(request, 'myapp/index.html', context=context)
if request.method == 'POST':
content = request.POST.get('content')
# content=escape(content)
tags = ALLOWED_TAGS + ['img']
attributes = {**ALLOWED_ATTRIBUTES, 'img': ['src']}
cleaned_data = bleach.clean(content, tags=tags, attributes=attributes)
# cleaned_data = bleach.clean(content)
# School.objects.create(content=content)
School.objects.create(content=cleaned_data)
return redirect(reverse('comment'))
同樣繼續(xù)執(zhí)行后輸入script代碼,執(zhí)行后結(jié)果:
0.5.png
數(shù)據(jù)庫(kù)中:
1.png
上面因?yàn)檗D(zhuǎn)義掉了大于小于號(hào),所以就無(wú)法輸出圖片,接下來(lái)我們繼續(xù)輸入這段js代碼:
<img src='http://img.haote.com/upload/news/image/20170605/20170605144101_12960.jpg' />,然后結(jié)果如下:
2.png
由上面可知,script是bleach默認(rèn)不允許的,被bleach庫(kù)轉(zhuǎn)義了,而我們手動(dòng)放松了img標(biāo)簽的通過(guò),這樣就
達(dá)到了我們隨心所欲的控制html標(biāo)簽的轉(zhuǎn)義與否?。?br>
此時(shí)數(shù)據(jù)庫(kù)中:









