Http基礎二 Web安全簡介 SQL注入 XSS CSRF(token)

參考
Web安全之SQL注入攻擊技巧與防範
總結 XSS 與 CSRF 兩種跨站攻擊
CSRF的攻擊與防禦
CSRF 攻擊的應對之道

一、SQL注入

用Web網站中常用的會員登錄系統來做一個場景實例。如果輸入正確的用戶名 plhwin 和密碼 123456,執行的SQL語句爲:SELECT uid,username FROM user WHERE username='plhwin' AND password='...' 但如果有搗蛋鬼輸入的用戶名爲 plhwin' AND 1=1-- hack,密碼隨意輸入,比如aaaaaa,那麼拼接之後的SQL查詢語句就變成了如下內容:SELECT uid,username FROM user WHERE username='plhwin' AND 1=1-- hack' AND password='...'。執行上面的SQL語句,因爲1=1是永遠成立的條件,這意味着黑客只需要知道別人的會員名,無需知道密碼就能順利登錄到系統。

解決方式:

  • 檢查變量數據類型和格式
  • 過濾特殊符號
  • 參數化查詢,使用預編譯語句

二、XSS

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

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

 

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

也可以是盜號或者其他未授權的操作。每個訪問到含有該評論的頁面的用戶都會遇到麻煩——他們不知道背後正悄悄的發起了一個請求,是他們所看不到的。而這個請求,會把包含了他們的帳號和其他隱私的信息發送到收集服務器上。

解決方式:過濾輸入和轉義輸出。

三、CSRF

CSRF攻擊原理比較簡單,如圖1所示。其中Web A爲存在CSRF漏洞的網站,Web B爲攻擊者構建的惡意網站,User C爲Web A網站的合法用戶。

圖1 CSRF攻擊原理

  • 用戶C打開瀏覽器,訪問受信任網站A,輸入用戶名和密碼請求登錄網站A;
  • 在用戶信息通過驗證後,網站A產生Cookie信息並返回給瀏覽器,此時用戶登錄網站A成功,可以正常發送請求到網站A;
  • 用戶未退出網站A之前,在同一瀏覽器中,打開一個TAB頁訪問網站B;
  • 網站B接收到用戶請求後,返回一些攻擊性代碼,併發出一個請求要求訪問第三方站點A;
  • 瀏覽器在接收到這些攻擊性代碼後,根據網站B的請求,在用戶不知情的情況下攜帶Cookie信息,向網站A發出請求。網站A並不知道該請求其實是由B發起的,所以會根據用戶C的Cookie信息以C的權限處理該請求,導致來自網站B的惡意代碼被執行。

再舉個例子,一論壇網站的發貼是通過 GET 請求訪問,點擊發貼之後 JS 把發貼內容拼接成目標 URL 並訪問: http://example.com/bbs/create_post.php?title=標題&content=內容 那麼,我只需要在論壇中發一帖,包含一鏈接: http://example.com/bbs/create_post.php?title=我是腦殘&content=哈哈 只要有用戶點擊了這個鏈接,那麼他們的帳戶就會在不知情的情況下發布了這一帖子。可能這只是個惡作劇,但是既然發貼的請求可以僞造,那麼刪帖、轉帳、改密碼、發郵件全都可以僞造。

如何解決這個問題,我們是否可以效仿上文應對 XSS 的做法呢?過濾用戶輸入, 不允許發佈這種含有站內操作 URL 的鏈接。這麼做可能會有點用,但阻擋不了 CSRF,因爲攻擊者可以通過 QQ 或其他網站把這個鏈接發佈上去,爲了僞裝可能還使用 bit.ly 壓縮一下網址,這樣點擊到這個鏈接的用戶還是一樣會中招。所以對待 CSRF ,我們的視角需要和對待 XSS 有所區別。CSRF 並不一定要有站內的輸入,因爲它並不屬於注入攻擊,而是請求僞造。被僞造的請求可以是任何來源,而非一定是站內。所以我們唯有一條路可行,就是過濾請求的處理者。

比較頭痛的是,因爲請求可以從任何一方發起,而發起請求的方式多種多樣,可以通過 iframe、ajax(這個不能跨域,得先 XSS)、Flash 內部發起請求(總是個大隱患)。由於幾乎沒有徹底杜絕 CSRF 的方式,我們一般的做法,是以各種方式提高攻擊的門檻。

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

接下來我們就可以用比較簡單也比較有效的方法來防禦 CSRF,這個方法就是“請求令牌”。讀過《J2EE 核心模式》的同學應該對“同步令牌”應該不會陌生,“請求令牌”和“同步令牌”原理是一樣的,只不過目的不同,後者是爲了解決 POST 請求重複提交問題,前者是爲了保證收到的請求一定來自預期的頁面。實現方法非常簡單,首先服務器端要以某種策略生成隨機字符串,作爲令牌(token),保存在 Session 裏。然後在發出請求的頁面,把該令牌以隱藏域一類的形式,與其他信息一併發出。在接收請求的頁面,把接收到的信息中的令牌與 Session 中的令牌比較,只有一致的時候才處理請求,否則返回 HTTP 403 拒絕請求或者要求用戶重新登錄驗證身份。

以下參考我想知道token和sessionid的區別是什麼

PrideChung 2013-08-23 15:41:03 +08:00
搞清楚CSRF攻擊的原理就行了。session id能夠標識一個用戶,卻無法知道該用戶提交的表單是自願主動提交的,還是被騙去點了個鏈接,被惡意的JS提交的,所以才需要CSRF token。

zzNucker 2013-08-25 13:51:17 +08:00
@leonwong 就算是CSRF劫持的請求也會帶上網站的cookie的,所以光驗證session並不能避免CSRF。 token的關鍵是在於你在發送請求的時候一定要保證你是讀取了這個頁面的。。 而不是憑空就發送請求

etata 40 天前
@binux
還是沒說爲什麼用 token ,不用 sessionid 啊。既然唯一 id ,放在 cookies 也是放,放在 hidden 裏也是放。再說 sessionid 在 cookies 禁止的時候也是放在 url 中或者表單中啊。 爲什麼不統一用一個呢?

binux 40 天前
@etata 唯一 id 一般是固定的, token 可以不是固定的,而是每次訪問隨機生成的。當然你要把 seesionid 用作 token 也不是不可以。

發佈了96 篇原創文章 · 獲贊 34 · 訪問量 16萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章