跨站腳本(XSS)教學
跨站腳本攻擊的指南。它們是如何運作的?如何防止它們?
什麼是XSS,也就是跨站腳本?
XSS是我們用來定義一種特定類型的攻擊的術語,該攻擊利用網站(如果你不小心)作為攻擊用的向量,因為它處理用戶輸入時存在著不安全的方式。
基本上,壞人(攻擊者)可以以某種方式將JavaScript注入到我們的網站中,利用我們在代碼中留下的漏洞。
利用這個漏洞,他們可以竊取用戶的信息。
根據如何利用XSS漏洞,我們有三種主要的XSS攻擊方式:
* 持久XSS
* 反射XSS
* 基於DOM的XSS
為什麼XSS是危險的?
想像一下你有一個網站。一個攻擊者可以以某種方式注入JavaScript代碼到你的網站中,並且在你用戶的瀏覽器中執行-無需你的意願和知識。
這是非常危險的。
由於你沒能妥善解決XSS漏洞,你的網站可以被用作攻擊向量,你的用戶信息就會受到風險。
特別是,當任何人都可以在頁面中注入JavaScript時,他們可以訪問與網站相關聯的用戶cookie,並讀取其中的任何信息。並將其發送到自己的服務器。他們可以監聽鍵盤事件,並獲取用戶在頁面中輸入的任何內容,然後使用fetch或XHR將其發送到攻擊者的服務器。例如用戶名和密碼。他們還可以操縱DOM,利用這個能力他們可以做很多壞事。
XSS是前端還是後端問題?
兩者都是。這是一個涉及前端和後端的網站架構問題。
XSS攻擊的示例
XSS基本上是在你允許用戶輸入信息並將其存儲(在後端)後再次顯示的情況下啟用的。
假設你有一個博客,並且允許用戶對博客發表評論。如果你盲目接受用戶輸入的任何內容, 惡意用戶可以試圖輸入JavaScript片段,基本形式是用<script></script>括起來的。例如<script>alert(“test”)</script>。
你可能會將此評論存儲在數據庫中,而當頁面重新加載時-同樣,如果你沒有任何預防措施-加載該頁面的所有用戶都將運行該JavaScript片段。
我使用了一個簡單的alert()調用作為例子,但就像上面列舉的,用戶可能輸入任何類型的腳本。此時,網站就受到了入侵。
什麼是持久性XSS?
持久性XSS是我們在野外發現的三種XSS中的一種。這是我在博客文章示例中描述的那一種。
在此情況下,漏洞的代碼存儲在數據庫或由你自己託管的其他源中。
一旦有人可以輸入JavaScript片段,它就會自動由你的網站提供,不需要任何其他操作。
什麼是反射性XSS?
反射性XSS是一種利用你的網站漏洞即時提供給最終用戶帶有腳本的鏈接的方式。
通過這種方式,攻擊者提供了一個類似於
1 | yoursite.com/?example=<script>alert('test')</script> |
如果你的網站使用example
GET變量執行某些操作並在頁面上顯示它,而你沒有檢查和清理它的值,那麼該腳本將被用戶的瀏覽器執行。
典型的例子是搜索表單。它可能位於/search
URL中,並且您可能會使用GET term
變量來接受搜索詞。
當有人搜索時,您可能會顯示You searched for <term>
字符串。現在,如果你沒有對值進行清理,那麼你現在就有問題了。
垃圾郵件/釣魚郵件是這種XSS攻擊常用的媒介。當然,網站越大越重要,黑客就越頻繁地嘗試入侵它。
什麼是基於DOM的XSS?
通過持久XSS和反射XSS,攻擊代碼必須被發送到服務器,並且它可能(並且希望是)經過了過濾。
基於DOM的XSS是一種攻擊代碼從未被發送到服務器的XSS。這種情況通常通過使用URL的片段部分或引用document.URL
/ document.location.href
來實現。網上找到的一些例子現在不再起作用,因為現代瀏覽器會自動對地址欄中的JS進行轉義。只有在解碼後才能正常工作,這也有點可怕(不要這樣做!)。
這裡有一個簡單的工作示例。假設您有一個頁面聽取位於http://127.0.0.1:8081/testxss.html上的test
變量:
http://127.0.0.1:8081/testxss.html#test=something
#test=something
值從不被發送到服務器。它只是本地值。持久/反射的XSS不起作用。但是,假設您的腳本使用以下方式訪問該值:
1 | const pos = document.URL.indexOf("test=") + 5; |
然後將其直接寫入DOM中:
1 | document.write(value) |
一切都好,直到有人以這樣的方式調用URL:
http://127.0.0.1:8081/testxss.html#test=alert(‘x’)
現在,由於引用document.URL
自動轉義的方式,這種情況下不應該發生任何事情。
輸出到頁面的內容應該是
1 | %3Cscript%3Ealert('x')%3C/script%3E |
值已被轉義,因此不會被解釋為HTML。
但是,如果由於某種原因您對document.URL
值進行解碼,那麼您現在就有問題了,因為JavaScript將被執行。任何JS代碼都可以在您的用戶瀏覽器上運行。
在舊瀏覽器上,這是一個更大的問題,因為它們不會自動對地址欄中的JS進行轉義。
靜態網站是否容易受到XSS攻擊?
是的!實際上,任何類型的網站都容易受到XSS攻擊。因為靜態意味著沒有從其他來源加載信息。例如,你可以自己實現表單或評論,即使沒有數據庫。
或者,我們可能有一個接受來自HTTP GET或POST變量的輸入的搜索功能。沒有數據庫並不意味著你免疫。
如何預防XSS?
有三種技術可以使用:
* 編碼
* 校驗
* CSP
編碼用於對數據進行轉義。這樣,瀏覽器將不會解釋JavaScript,例如 <script>
將被編碼為 %3Cscript%3E
。
編碼作為一般規則,應該始終進行。
服務器端框架通常提供輔助功能來為您提供此功能。
在客戶端JavaScript中,我們根據用例使用不同的編碼機制。
**如果你需要向HTML元素中添加內容**,最好的方法是使用`textContent`屬性將用戶生成的輸入分配給該元素。瀏覽器將為您進行所有的轉義:
1
document.querySelector('#myElement').textContent = theUserGeneratedInput
**如果你需要創建一個元素**,使用`document.createTextNode()`:
1
const el = document.createTextNode(theUserGeneratedInput)
**如果你需要向HTML屬性中添加內容**,使用元素的`setAttribute()`方法:
1
document.querySelector('#myElement').setAttribute('attributeName', theUserGeneratedInput)
如果你需要向URL添加內容,使用[`window.encodeURIComponent()`](/如何編碼網址/)函數:
1
window.location.href = window.location.href + '?test=' + window.encodeURIComponent(theUserGeneratedInput)
校驗通常用於在無法使用轉義來過濾輸入時。常見的例子是允許用戶在HTML中定義頁面內容的CMS。你無法進行轉義。
校驗可以使用黑名單或白名單策略進行。不同之處在於,黑名單策略決定禁止哪些標籤,而白名單策略則決定允許哪些標籤。白名單策略更安全,因為黑名單策略容易出錯,且過於複雜,也不具備未來擴展性。
CSP表示內容安全策略。這是瀏覽器實施的一個新標準,用於強制執行僅從安全和可信源頭執行JavaScript代碼,並且可以禁止在代碼中運行內嵌JavaScript,例如上面的XSS攻擊。
通過在頁面提供時添加Content‑Security‑Policy
HTTP標頭,Web服務器啟用CSP。