1. 程式人生 > 實用技巧 >網路安全

網路安全

XSS

XSS(Cross site scripting) 跨站指令碼攻擊

網站存在漏洞,允許惡意使用者注入惡意程式碼

例子,網站允許 img src 路徑的拼接;寫入可執行指令碼

innerHTML 容易引發跨站指令碼攻擊

CSRF

什麼是 CSRF

CSRF(Cross-site request forgery) 跨站請求偽造:攻擊者誘導受害者進入第三方網站,在第三方網站中,向被攻擊網站傳送跨站請求。利用受害者在被攻擊網站已經獲取的註冊憑證,繞過後臺的使用者驗證,達到冒充使用者對被攻擊的網站執行某項操作的目的

一個典型的 CSRF 攻擊有著如下的流程:

  • 受害者登入a.com,並保留了登入憑證(Cookie)。
  • 攻擊者引誘受害者訪問了b.com。
  • b.com 向 a.com 傳送了一個請求:a.com/act=xx。瀏覽器會預設攜帶 a.com 的Cookie
  • a.com接收到請求後,對請求進行驗證,並確認是受害者的憑證,誤以為是受害者自己傳送的請求
  • a.com以受害者的名義執行了act=xx
  • 攻擊完成,攻擊者在受害者不知情的情況下,冒充受害者,讓a.com執行了自己定義的操作

CSRF的特點

  • 攻擊一般發起在第三方網站,而不是被攻擊的網站。被攻擊的網站無法防止攻擊發生
  • 攻擊利用受害者在被攻擊網站的登入憑證,冒充受害者提交操作;而不是直接竊取資料
  • 整個過程攻擊者並不能獲取到受害者的登入憑證,僅僅是“冒用”
  • 跨站請求可以用各種方式:圖片URL、超連結、CORS、Form提交等等。部分請求方式可以直接嵌入在第三方論壇、文章中,難以進行追蹤

CSRF通常是跨域的,因為外域通常更容易被攻擊者掌控。但是如果本域下有容易被利用的功能,比如可以發圖和連結的論壇和評論區,攻擊可以直接在本域下進行,而且這種攻擊更加危險

防護策略

CSRF通常從第三方網站發起,被攻擊的網站無法防止攻擊發生,只能通過增強自己網站針對CSRF的防護能力來提升安全性

前面提到了 CSRF的兩個特點

  • CSRF(通常)發生在第三方域名
  • CSRF攻擊者不能獲取到Cookie等資訊,只是使用

針對這兩點,我們可以專門制定防護策略

  • 阻止不明外域的訪問
  • 同源檢測
  • Samesite Cookie
  • 提交時要求附加本域才能獲取的資訊
  • CSRF Token(所有使用者的請求再攜帶一個CSRF攻擊者無法獲取到的Token,伺服器通過校驗請求是否攜帶正確的Token,來把正常的請求和攻擊的請求區分開)
  • 雙重Cookie驗證

前面講到CSRF的另一個特徵是,攻擊者無法直接竊取到使用者的資訊(Cookie,Header,網站內容等),僅僅是冒用Cookie中的資訊。

而CSRF攻擊之所以能夠成功,是因為伺服器誤把攻擊者傳送的請求當成了使用者自己的請求。那麼我們可以要求所有的使用者請求都攜帶一個CSRF攻擊者無法獲取到的Token。伺服器通過校驗請求是否攜帶正確的Token,來把正常的請求和攻擊的請求區分開,也可以防範CSRF的攻擊。

CSRF Token的防護策略分為三個步驟

  1. 將CSRF Token輸出到頁面中

    首先,使用者開啟頁面的時候,伺服器需要給這個使用者生成一個Token,該Token通過加密演算法對資料進行加密,一般Token都包括隨機字串和時間戳的組合,顯然在提交時Token不能再放在Cookie中了,否則又會被攻擊者冒用。因此,為了安全起見Token最好還是存在伺服器的Session中,之後在每次頁面載入時,使用JS遍歷整個DOM樹,對於DOM中所有的a和form標籤後加入Token。這樣可以解決大部分的請求,但是對於在頁面載入之後動態生成的HTML程式碼,這種方法就沒有作用,還需要程式設計師在編碼時手動新增Token。

  2. 頁面提交的請求攜帶這個Token

    對於GET請求,Token將附在請求地址之後,這樣URL 就變成 http://url?csrftoken=tokenvalue。 而對於 POST 請求來說,要在 form 的最後加上: 這樣,就把Token以引數的形式加入請求了

  3. 伺服器驗證Token是否正確

    當用戶從客戶端得到了Token,再次提交給伺服器的時候,伺服器需要判斷Token的有效性,驗證過程是先解密Token,對比加密字串以及時間戳,如果加密字串一致且時間未過期,那麼這個Token就是有效的。

    這種方法要比之前檢查Referer或者Origin要安全一些,Token可以在產生並放於Session之中,然後在每次請求時把Token從Session中拿出,與請求中的Token進行比對,但這種方法的比較麻煩的在於如何把Token以引數的形式加入請求。

    下面將以Java為例,介紹一些CSRF Token的服務端校驗邏輯,程式碼如下:

    HttpServletRequestreq=(HttpServletRequest)request;
    HttpSessions=req.getSession();

    //從session中得到csrftoken屬性
    StringsToken=(String)s.getAttribute(“csrftoken”);
    if(sToken==null){
    //產生新的token放入session中
    sToken=generateToken();
    s.setAttribute(“csrftoken”,sToken);
    chain.doFilter(request,response);
    }else{
    //從HTTP頭中取得csrftoken
    StringxhrToken=req.getHeader(“csrftoken”);
    //從請求引數中取得csrftoken
    StringpToken=req.getParameter(“csrftoken”);
    if(sToken!=null&&xhrToken!=null&&sToken.equals(xhrToken)){
    chain.doFilter(request,response);
    }elseif(sToken!=null&&pToken!=null&&sToken.equals(pToken)){
    chain.doFilter(request,response);
    }else{
    request.getRequestDispatcher(“error.jsp”).forward(request,response);
    }
    }

    這個Token的值必須是隨機生成的,這樣它就不會被攻擊者猜到,考慮利用Java應用程式的java.security.SecureRandom類來生成足夠長的隨機標記,替代生成演算法包括使用256位BASE64編碼雜湊,選擇這種生成演算法的開發人員必須確保在雜湊資料中使用隨機性和唯一性來生成隨機標識。通常,開發人員只需為當前會話生成一次Token。在初始生成此Token之後,該值將儲存在會話中,並用於每個後續請求,直到會話過期。當終端使用者發出請求時,伺服器端必須驗證請求中Token的存在性和有效性,與會話中找到的Token相比較。如果在請求中找不到Token,或者提供的值與會話中的值不匹配,則應中止請求,應重置Token並將事件記錄為正在進行的潛在CSRF攻擊。

分散式校驗

在大型網站中,使用Session儲存CSRF Token會帶來很大的壓力。訪問單臺伺服器session是同一個。但是現在的大型網站中,我們的伺服器通常不止一臺,可能是幾十臺甚至幾百臺之多,甚至多個機房都可能在不同的省份,使用者發起的HTTP請求通常要經過像Ngnix之類的負載均衡器之後,再路由到具體的伺服器上,由於Session預設儲存在單機伺服器記憶體中,因此在分散式環境下同一個使用者傳送的多次HTTP請求可能會先後落到不同的伺服器上,導致後面發起的HTTP請求無法拿到之前的HTTP請求儲存在伺服器中的Session資料,從而使得Session機制在分散式環境下失效,因此在分散式叢集中CSRF Token需要儲存在Redis之類的公共儲存空間。由於使用Session儲存,讀取和驗證CSRF Token會引起比較大的複雜度和效能問題,目前很多網站採用Encrypted Token Pattern方式。這種方法的Token是一個計算出來的結果,而非隨機生成的字串。這樣在校驗時無需再去讀取儲存的Token,只用再次計算一次即可。這種Token的值通常是使用UserID、時間戳和隨機數,通過加密的方法生成。這樣既可以保證分散式服務的Token一致,又能保證Token不容易被破解。在token解密成功之後,伺服器可以訪問解析值,Token中包含的UserID和時間戳將會被拿來被驗證有效性,將UserID與當前登入的UserID進行比較,並將時間戳與當前時間進行比較。

雙重Cookie驗證

在會話中儲存CSRF Token比較繁瑣,而且不能在通用的攔截上統一處理所有的介面那麼另一種防禦措施是使用雙重提交Cookie。利用CSRF攻擊不能獲取到使用者Cookie的特點,(攻擊者並利用而沒有獲取),我們可以要求Ajax和表單請求攜帶一個Cookie中的值。

雙重Cookie採用以下流程:

  • 在使用者訪問網站頁面時,向請求域名注入一個Cookie,內容為隨機字串(例如csrfcookie=v8g9e4ksfhw)。

  • 在前端向後端發起請求時,取出Cookie,並新增到URL的引數中(接上例POST https://www.a.com/comment?csrfcookie=v8g9e4ksfhw)。// 讀取並使用Cookie中的資訊,這是csrf攻擊者做不到的

  • 後端介面驗證Cookie中的欄位與URL引數中的欄位是否一致,不一致則拒絕。此方法相對於CSRF Token就簡單了許多。可以直接通過前後端攔截的的方法自動化實現。後端校驗也更加方便,只需進行請求中欄位的對比,而不需要再進行查詢和儲存Token。

clickjacking

ClickJacking(點選劫持)是由網際網路安全專家羅伯特·漢森和耶利米·格勞斯曼在2008年首創的。ClickJacking是一種視覺欺騙攻擊手段,在web端就是iframe巢狀一個透明不可見的頁面,讓使用者在不知情(被欺騙)的情況下,點選攻擊者想要欺騙使用者點選的位置。(使用者點選之後,就可以開啟使用者的攝像頭或者幹一些其他操作…)

X-Frame-Options HTTP 響應頭是用來給瀏覽器 指示允許一個頁面 可否在 <frame>, <iframe>, <embed>或者 <object> 中展現的標記。站點可以通過 確保網站沒有被嵌入到別人的站點 裡面,從而避免 clickjacking攻擊

X-Frame-Options:deny
X-Frame-Options:sameorigin
X-Frame-Options:allow-fromhttps://example.com/

X-Frame-Options 有三個可能的值:

  1. deny

    表示該頁面不允許在 frame 中展示,即便是在相同域名的頁面中巢狀也不允許。

  2. sameorigin

    表示該頁面可以在相同域名頁面的 frame 中展示。

  3. allow-from uri

    表示該頁面可以在指定來源的 frame 中展示。