1. 程式人生 > >前端進階(五)web安全

前端進階(五)web安全

一、XSS

xss: 跨站指令碼攻擊(Cross Site Scripting)是最常見和基本的攻擊 WEB 網站方法,攻擊者通過注入非法的 html 標籤或者 javascript 程式碼,從而當用戶瀏覽該網頁時,控制使用者瀏覽器。

xss 主要分為三類:

1、DOM xss :

DOM即文字物件模型,DOM通常代表在html、xhtml和xml中的物件,使用DOM可以允許程式和指令碼動態的訪問和更新文件的內容、結構和樣式。它不需要伺服器解析響應的直接參與,觸發XSS靠的是瀏覽器端的DOM解析,可以認為完全是客戶端的事情。

2、反射型 xss :

反射型XSS也被稱為非永續性XSS,是現在最容易出現的一種XSS漏洞。發出請求時,XSS程式碼出現在URL中,最後輸入提交到伺服器,伺服器解析後在響應內容中出現這段XSS程式碼,最後瀏覽器解析執行。

3、儲存型 xss :

儲存型XSS又被稱為永續性XSS,它是最危險的一種跨站指令碼,相比反射型XSS和DOM型XSS具有更高的隱蔽性,所以危害更大,因為它不需要使用者手動觸發。 允許使用者儲存資料的web程式都可能存在儲存型XSS漏洞,當攻擊者提交一段XSS程式碼後,被伺服器端接收並存儲,當所有瀏覽者訪問某個頁面時都會被XSS,其中最典型的例子就是留言板。

跨站指令碼攻擊可能造成以下影響:

  • 利用虛假輸入表單騙取使用者個人資訊。
  • 利用指令碼竊取使用者的 Cookie 值,被害者在不知情的情況下,幫助攻擊者傳送惡意請求。
  • 顯示偽造的文章或圖片。

二、CSRF

跨站請求偽造 (Cross-Site Request Forgery) 是一種偽造跨站請求的攻擊方式. 例如利用你在 A 站 (攻擊目標) 的 cookie / 許可權等, 在 B 站 (惡意/釣魚網站) 拼裝 A 站的請求.

已知某站點 A 刪除的介面是 get 到某個地址, 並指定一個帖子的 id. 在網站 B 上組織一個刪除A站某文章的get請求. 然後A站使用者訪問B站,觸發該請求. 就可以不知情的情況下刪除某個帖子.

同源策略是最早用於防止 CSRF 的一種方式, 即關於跨站請求 (Cross-Site Request) 只有在同源/信任的情況下才可以請求. 但是如果一個網站群, 在互相信任的情況下, 某個網站出現了問題:

a.public.com
b.public.com
c.public.com
...

以上情況下, 如果 c.public.com 上沒有預防 xss 等情況, 使得攻擊者可以基於此站對其他信任的網站發起 CSRF 攻擊.

另外同源策略主要是瀏覽器來進行驗證的, 並且不同瀏覽器的實現又各自不同, 所以在某些瀏覽器上可以直接繞過, 而且也可以直接通過簡訊等方式直接繞過瀏覽器.

預防:

  • 在 HTTP 頭中自定義屬性並驗證

  • 檢查 CSRF token.

  • cookie中加入hash隨機數.

  • 通過檢查來過濾簡單的 CSRF 攻擊, 主要檢查一下兩個 header:

  • Origin Header

  • Referer Header

三、SQL/NoSQL 注入

注入攻擊是指當所執行的一些操作中有部分由使用者傳入時, 使用者可以將其惡意邏輯注入到操作中. 當你使用 eval, new Function 等方式執行的字串中有使用者輸入的部分時, 就可能被注入攻擊. 上文中的 XSS 就屬於一種注入攻擊. 前面的章節中也提到過 Node.js 的 child_process.exec 由於呼叫 bash 解析, 如果執行的命令中有部分屬於使用者輸入, 也可能被注入攻擊.

SQL

Sql 注入是網站常見的一種注入攻擊方式. 其原因主要是由於登入時需要驗證使用者名稱/密碼, 其執行 sql 類似:

SELECT * FROM users WHERE usernae = 'myName' AND password = 'mySecret';

其中的使用者名稱和密碼屬於使用者輸入的部分, 那麼在未做檢查的情況下, 使用者可能拼接惡意的字串來達到其某種目的, 例如上傳密碼為 '; DROP TABLE users; -- 使得最終執行的內容為:

SELECT * FROM users WHERE usernae = 'myName' AND password = ''; DROP TABLE users; --';

其能實現的功能, 包括但不限於刪除資料 (經濟損失), 篡改資料 (密碼等), 竊取資料 (網站管理許可權, 使用者資料) 等. 防治手段常見於:

  • 給表名/欄位名加字首 (避免被猜到)

  • 報錯隱藏表資訊 (避免被看到, 12306 早起就出現過的問題)

  • 過濾可以拼接 SQL 的關鍵字元

  • 對使用者輸入進行轉義

  • 驗證使用者輸入的型別 (避免 limit, order by 等注入)

NoSQL

看個簡單的情況:

let {user, pass, age} = ctx.query;

db.collection.find({
  user, pass,
  $where: `this.age >= ${age}`
})

那麼這裡的 age 就可以注入了. 另外 GET/POST 還可以傳遞深層結構 (比如 ?name[0]=alan 傳遞上來), 通過 qs 之類的模組解析後導致注入, 如 cnodejs 遭遇 mongodb 注入.

四、Crypto

Node.js 的 crypto 模組封裝了諸多的加密功能, 包括 OpenSSL 的雜湊、HMAC、加密、解密、簽名和驗證函式等.

加密是如何保證使用者密碼的安全性?

在客戶端加密, 是增加傳輸的過程中被第三方嗅探到密碼後破解的成本. 對於遊戲, 在客戶端加密是防止外掛/破解等. 在服務端加密 (如 md5) 是避免管理資料庫的 DBA 或者攻擊者攻擊資料庫之後直接拿到明文密碼, 從而提高安全性.

五、TLS/SSL

早期的網路傳輸協議由於只在大學內使用, 所以是預設互相信任的. 所以傳統的網路通訊可以說是沒有考慮網路安全的. 早年的瀏覽器大廠網景公司為了應對這個情況設計了 SSL (Secure Socket Layer), SSL 的主要用途是:

  1. 認證使用者和伺服器, 確保資料傳送到正確的客戶機和伺服器;

  2. 加密資料以防止資料中途被竊取;

  3. 維護資料的完整性, 確保資料在傳輸過程中不被改變.

存在三個特性:

  • 機密性:SSL協議使用金鑰加密通訊資料

  • 可靠性:伺服器和客戶都會被認證, 客戶的認證是可選的

  • 完整性:SSL協議會對傳送的資料進行完整性檢查

1999年, SSL 因為應用廣泛, 已經成為網際網路上的事實標準. IETF 就在那年把 SSL 標準化/強化. 標準化之後的名稱改為傳輸層安全協議 (Transport Layer Security, TLS). 很多相關的文章都把這兩者並列稱呼 (TLS/SSL), 因為這兩者可以視作同一個東西的不同階段.

六、Https

在網路上, 每個網站都在各自的伺服器上, 想要確保你訪問的是一個正確的網站, 並且訪問到這個網站正確的資料 (沒有被劫持/篡改), 除了需要傳輸安全之外, 還需要安全的認證, 認證不能由目標網站進行, 否則惡意/釣魚網站也可以自己說自己是對的, 所以為了能在網路上維護網路之間的基本信任, 早期的大廠們合力推動了一項名為 PKI 的基礎設施, 通過第三方來認證網站.

公鑰基礎設施 (Public Key Infrastructure, PKI) 是一種遵循標準的, 利用公鑰加密技術為電子商務的開展提供一套安全基礎平臺的技術和規範. 其基礎建置包含認證中心 (Certification Authority, CA) 、註冊中心 (Register Authority, RA) 、目錄服務 (Directory Service, DS) 伺服器.

由 RA 統籌、稽核使用者的證書申請, 將證書申請送至 CA 處理後發出證書, 並將證書公告至 DS 中. 在使用證書的過程中, 除了對證書的信任關係與證書本身的正確性做檢查外, 並透過產生和釋出證書廢止列表 (Certificate Revocation List, CRL) 對證書的狀態做確認檢查, 瞭解證書是否因某種原因而遭廢棄. 證書就像是個人的身分證, 其內容包括證書序號、使用者名稱稱、公開金鑰 (Public Key) 、證書有效期限等.

在 TLS/SLL 中你可以使用 OpenSSL 來生成 TLS/SSL 傳輸時用來認證的 public/private key. 不過這個 public/private key 是自己生成的, 而通過 PKI 基礎設施可以獲得權威的第三方證書 (key) 從而加密 HTTP 傳輸安全. 目前部落格圈子裡比較流行的是 Let's Encrypt 簽發免費的 HTTPS 證書.

需要注意的是, 如果 PKI 受到攻擊, 那麼 HTTPS 也一樣不安全. 可以參見 HTTPS 劫持 - 知乎討論 中的情況, 證書由 CA 機構簽發, 一般瀏覽器遇到非權威的 CA 機構是會告警的 (參見 12306), 但是如果你在某些特殊的情況下信任了某個未知機構/證書, 那麼也可能被劫持.

此外有的 CA 機構以郵件方式認證, 那麼當某個網站的郵件服務收到攻擊/滲透, 那麼攻擊者也可能以此從 CA 機構獲取權威的正確的證書.

七、DDOS

DDoS即Distributed Denial of Service,分散式拒絕服務。也就是攻擊者藉助或者利用伺服器技術,將多個計算機(肉雞或殭屍機)聯合起來作為攻擊平臺,對一個或者多個目標伺服器,同一時間傳送大量垃圾資訊,或利用某種干擾資訊的方式,導致目標伺服器無法及時響應正常使用者正常請求,或者直接導致目標伺服器宕機,從而無法為正常使用者提供服務的一種攻擊行為。

攻擊方式:

  • 埠掃描攻擊

  • ping洪水(flooding)攻擊

  • SYN洪水(flooding)攻擊

  • FTP跳轉攻擊

防範手段:

  • 保證伺服器系統的安全,確保伺服器軟體沒有任何漏洞,防止攻擊者入侵。

  • 確保伺服器採用最新系統,並打上安全補丁。

  • 在伺服器上刪除未使用的服務,關閉未使用的埠。

  • 對於伺服器上執行的網站,確保其打了最新的補丁,沒有安全漏洞。

  • 隱藏伺服器的真實IP地址

八、Clickjacking

Clickjacking: 點選劫持,是指利用透明的按鈕或連線做成陷阱,覆蓋在 Web 頁面之上。然後誘使使用者在不知情的情況下,點選那個連線訪問內容的一種攻擊手段。這種行為又稱為介面偽裝(UI Redressing) 。

大概有兩種方式:

  • 攻擊者使用一個透明 iframe,覆蓋在一個網頁上,然後誘使使用者在該頁面上進行操作,此時使用者將在不知情的情況下點選透明的 iframe 頁面;
  • 攻擊者使用一張圖片覆蓋在網頁,遮擋網頁原有的位置含義。

案例

一張圖瞭解

一般步驟

  • 黑客建立一個網頁利用 iframe 包含目標網站;
  • 隱藏目標網站,使使用者無法無法察覺到目標網站存在;
  • 構造網頁,誘變使用者點選特點按鈕
  • 使用者在不知情的情況下點選按鈕,觸發執行惡意網頁的命令。

防禦

  • X-FRAME-OPTIONS;

    X-FRAME-OPTIONS HTTP 響應頭是用來給瀏覽器指示允許一個頁面可否在<frame><iframe> 或者 <object> 中展現的標記。網站可以使用此功能,來確保自己網站內容沒有被嵌到別人的網站中去,也從而避免點選劫持的攻擊。

    有三個值:

    • DENY:表示頁面不允許在 frame 中展示,即便是在相同域名的頁面中巢狀也不允許。
    • SAMEORIGIN:表示該頁面可以在相同域名頁面的 frame 中展示。
    • ALLOW-FROM url:表示該頁面可以在指定來源的 frame 中展示。

配置 X-FRAME-OPTIONS:

- Apache 

把下面這行新增到 'site' 的配置中:

```
Header always append X-Frame-Options SAMEORIGIN
```

- nginx

把下面這行新增到 'http', 'server' 或者 'location',配置中

```
add_header X-Frame-Options SAMEORIGIN;
```

- IIS

新增下面配置到 Web.config 檔案中

```
    <system.webServer>
  ...

  <httpProtocol>
    <customHeaders>
      <add name="X-Frame-Options" value="SAMEORIGIN" />
    </customHeaders>
  </httpProtocol>

  ...
</system.webServer>

```
  • js 判斷頂層視窗跳轉,可輕易破解,意義不大;
function locationTop(){
     if (top.location != self.location) {
        top.location = self.location; return false;
     }
     return true; 
    }
locationTop();
// 破解:
// 頂層視窗中放入程式碼
var location = document.location;
//或者
var location = "";

九、前端URL和文字框過濾器(防止sql注入和xss攻擊過濾)

_checkInputSQLInjectionAttack(oField){
    let re = /select|update|delete|exec|count|'|"|=|;|>|<|%/i;
    if (re.test(oField.value)) {
      openNotificationWithIcon("error");
      oField.value = '';
      oField.focus();
      return false;
    }
  }

  _checkUrlSQLInjectionAttack() {
    let sUrl = window.location.search.toLowerCase();
    let sQuery = sUrl.substring(sUrl.indexOf("=") + 1);
    let re = /select|update|delete|truncate|join|union|exec|insert|drop|count|'|"|;|>|<|%/i;
    if (re.test(sQuery)) {
      openNotificationWithIcon("error");
      window.location.href = sUrl.replace(sQuery, "");
    }
  }
componentDidMount() {
    var that = this;
    let eventDelegation = document.querySelectorAll('input');
    if(window.addEventListener){
      for(let i = 0, l = eventDelegation.length; i < l; ++i){
        eventDelegation[i].addEventListener('blur', (e) => {
          var target = e.target || e.srcElement;
          this._checkInputSQLInjectionAttack(target);
        }, false);
      }
    }else{
      for (let i = 0, l = eventDelegation.length; i < l; ++i) {
        eventDelegation[i].addEventListener('blur', (e) => {
          var target = e.target || e.srcElement;
          this._checkInputSQLInjectionAttack(target);
        }, false);
      }
    }
  }

ps:如果是React專案,input可以放在componentDidMount中完成,url可以放在初始化校驗url階段和請求資料前對介面進行過濾。