django下的xss攻擊原理分析和防御實(shí)戰(zhàn)

原理分析: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ì)有"<",">",取而代之的是“<”和“&gt”,其他字符同理。。。下面給出了escape()默認(rèn)轉(zhuǎn)義的字符,它是django內(nèi)定的:

_html_escapes = {
    ord('&'): '&amp;',
    ord('<'): '&lt;',
    ord('>'): '&gt;',
    ord('"'): '&quot;',
    ord("'"): '&#39;',
}

下面給出了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)簽:

image.png

attributes默認(rèn)允許的屬性:
image.png

  1. github地址: https://github.com/mozilla/bleach
  2. 文檔地址: 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ù)中:

3.png

本文參考多方面的文章,不可一一給出源地址,請(qǐng)見(jiàn)諒

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容