1. 程式人生 > >XSS 與 CSRF 兩種跨站攻擊

XSS 與 CSRF 兩種跨站攻擊

鏈接 href 本機 但是 不可見 不知道 pre col CI

在前幾年,大家一般用拼接字符串的方式來構造動態 SQL 語句創建應用,於是 SQL 註入成了很流行的攻擊方式, 但是現在參數化查詢 已經成了普遍用法,我們已經離 SQL 註入很遠了。但是歷史同樣悠久的 XSS 和 CSRF 卻任然存在。

XSS 全稱“跨站腳本”,是註入攻擊的一種。

其特點是不對服務器端造成任何傷害,通過一些正常的站內交互途徑,例如發布評論,提交含有 JavaScript 的內容文本。這時服務器端如果沒有過濾或轉義掉這些腳本,作為內容發布到了頁面上,其他用戶訪問這個頁面的時候就會運行這些腳本。

運行預期之外的腳本帶來的後果有很多中,可能只是簡單的惡作劇——一個關不掉的窗口:

while (true) {
    alert("你關不掉我~");
}

也可以是盜號或者其他未授權的操作。

AJAX 技術所使用的 XMLHttpRequest 對象都被瀏覽器做了限制,只能訪問當前域名下的 URL,所謂不能“跨域”問題。這種做法的初衷也是防範 XSS,但不是總是有用。一些瀏覽器能夠很智能地分析出部分 XSS 並予以攔截,防止 XSS 的根本之道還是過濾用戶輸入。用戶輸入總是不可信任的,這點對於 Web 開發者應該是常識。

XSS風險集中區域,是富 AJAX 類應用,並不集中在 HTTP 的靜態響應內容,所以不是開啟模版自動轉義能就能一勞永逸的。再加上這類應用往往需要跨域,開發者不得不自己打開危險的大門

(1)如果我們不需要用戶輸入 HTML 而只想讓他們輸入純文本,那麽把所有用戶輸入進行 HTML 轉義輸出是個不錯的做法。

PHP 可以用 htmlspecialchars 函數,模版引擎也必然自帶了方便快捷的轉義方式。

(2) 如果我們要允許用戶輸入 HTML,又要過濾其中的腳本。Tidy 等 HTML 清理庫可以幫忙,但前提是我們小心地使用。僅僅粗暴地去掉 script 標簽是沒有用的,任何一個合法 HTML 標簽都可以添加 onclick 一類的事件屬性來執行 JavaScript。比較推薦的方法是 白名單重新整理。 用戶輸入的 HTML 可能擁有很復雜的結構,HTML 解析庫遍歷節點,獲取其中數據,然後根據用戶原有的標簽屬性,重新構建 HTML 元素樹。所有的標簽、屬性都只從白名單中拿取,如果用戶的某種復雜輸入不能為解析器所識別,白名單重新整理的策略會直接丟棄掉這些未能識別的部分。

CSRF 的全稱是“跨站請求偽造”,是偽造請求,冒充用戶在站內的正常操作。和XSS一樣都是屬於跨站攻擊——不攻擊服務器端而攻擊正常訪問網站的用戶。

絕大多數網站是通過 cookie 等方式辨識用戶身份(包括使用服務器端 Session 的網站,因為 Session ID 也是大多保存在 cookie 裏面的),再予以授權的。所以要偽造用戶的正常操作,最好的方法是通過 XSS 或鏈接欺騙等途徑,讓用戶在本機(即擁有身份 cookie 的瀏覽器端)發起用戶所不知道的請求。

CSRF 並不一定要有站內的輸入,因為它並不屬於註入攻擊,而是請求偽造。被偽造的請求可以是任何來源,而非一定是站內。所以我們唯有一條路可行,就是過濾請求的 處理者。請求可以從任何一方發起,而發起請求的方式多種多樣,可以通過 iframe、ajax(這個不能跨域,得先 XSS)、Flash 內部發起請求(總是個大隱患)。由於幾乎沒有徹底杜絕 CSRF 的方式,我們一般的做法,是以各種方式提高攻擊的門檻。

(1) 改良站內 API 的設計, REST 風格 的 API 設計,GET、POST、PUT、DELETE 四種請求方法對應資源的讀取、創建、修改、刪除。現在的瀏覽器基本不支持在表單中使用 PUT 和 DELETE 請求方法,我們可以使用 ajax 提交請求(例如通過 jquery-form 插件,我最喜歡的做法),也可以使用隱藏域指定請求方法,然後用 POST 模擬 PUT 和 DELETE (Ruby on Rails 的做法)。這麽一來,不同的資源操作區分的非常清楚,我們把問題域縮小到了非 GET 類型的請求上——攻擊者已經不可能通過發布鏈接來偽造請求了,但他們仍可以發布表單,或者在其他站點上使用我們肉眼不可見的表單,在後臺用 js 操作,偽造請求。

(2)token “請求令牌”,令牌有兩種 “請求令牌”和“同步令牌”,後者是為了解決 POST 請求重復提交問題,前者是為了保證收到的請求一定來自預期的頁面。

服務器端要以某種策略生成隨機字符串,作為令牌(token), 保存在 Session ,發出請求的頁面,把該令牌以隱藏域一類的形式,與其他信息一並發出。在接收請求的頁面,把接收到的信息中的令牌與 Session 中的令牌比較,只有一致的時候才處理請求,否則返回 HTTP 403 拒絕請求或者要求用戶重新登陸驗證身份。

目前防禦 CSRF 的諸多方法還沒幾個能徹底無解的。所以 CSDN 上看到討論 CSRF 的文章,一般都會含有“無恥”二字來形容(另一位有該名號的貌似是 DDOS 攻擊)。作為開發者,我們能做的就是盡量提高破解難度。當破解難度達到一定程度,網站就逼近於絕對安全的位置了(雖然不能到達)。上述請求令牌方法,就我 認為是最有可擴展性的,因為其原理和 CSRF 原理是相克的。CSRF 難以防禦之處就在於對服務器端來說,偽造的請求和正常的請求本質上是一致的。而請求令牌的方法,則是揪出這種請求上的唯一區別——來源頁面不同。我們還可 以做進一步的工作,例如讓頁面中 token 的 key 動態化,進一步提高攻擊者的門檻。

XSS 與 CSRF 兩種跨站攻擊