Web 開發常見安全問題

不是所有 Web 開發者都有安全的概念,甚至可能某些安全漏洞從來都沒聽說過。這就是這篇科普文章的存在意義,希望 Web 開發者在開發時能依此逐條檢查代碼中的安全問題。

注:服務器運維相關的安全注意事項不在本文之列

這篇文章主要包含以下內容:

  • 前端安全

    • XSS 漏洞
    • CSRF 漏洞
  • 後端安全

    • SQL 注入漏洞
    • 權限控制漏洞
    • SESSION 與 COOKIE
    • IP 地址
    • 驗證碼

前言

水桶底部只要有一個洞,水就能全部流光。Web 安全同理。

前端安全

前端安全主要表現爲通過瀏覽器間接影響到用戶數據的安全問題。

  1. XSS 漏洞

    XSS (Cross-Site Scripting),是一個我覺得耳熟能詳的前端安全問題。通過構造特殊數據,在用戶瀏覽器上執行特定腳本,從而造成危害(如以用戶身份發帖、轉賬等)。

    由於頁面內 JavaScript 幾乎可以完成各種事情,因此一旦網站上有 XSS 漏洞,那些沒有驗證碼等確認措施的操作大多都能不知情地完成,其危害甚大。

    來看看幾個存在 XSS 漏洞的例子吧:

    1. Case A: HTML DOM

      <a href="/user/1">{{ user_name }}</a>

      Exploit:

      <script>alert(1)</script>
      

      Result:

      <a href="/user/1"><script>alert(1)</script></a>

      最基本的例子,如果此處不對 user_name 中的特殊符號進行 escape,就會造成 XSS。

    2. Case B: HTML Attribute

      <img src="{{ image_url }}">

      Exploit:

      " onerror="alert(1)
      

      Result:

      <img src="" onerror="alert(1)">

      這個例子表明,如果只對尖括號進行 escape 是不夠的,很多時候引號也需要被 escape。簡單來說,對不同輸出場景,需要使用不同的 escape 規則。

    3. Case C: JavaScript

      <script>var user_data = {{ user_data|json_encode }};</script>

      Exploit:

      {"exploit": "</script><script>alert(1);//"}
      

      Result:

      <script>var user_data = {"exploit": "</script><script>alert(1);//"};</script>

      這是一個特別的例子,大多數人覺得,對於輸出在 <script> 中的內容,json_encode 一下就安全了,其實不然。在這個例子中,XSS 仍然發生了。

      更可怕的是,不少編輯器的代碼高亮方案中甚至無法看出上面的 Result 中存在 XSS 漏洞。如 Sublime Text 下,代碼高亮結果是這樣的,看上去沒有任何問題:

      Sublime-Text-Highlight

      但是瀏覽器解析出來的結果是這樣的:

      Chrome-Inspector

    解決方法:

    1. 在不同上下文中,使用合適的 escape 方式

    2. 不要相信 任何 來自用戶的輸入(不僅限於 POST Body,還包括 QueryString,甚至是 Headers)

  2. CSRF 漏洞

    CSRF (Cross-site request forgery),是一個知名度不如 XSS 但是卻同樣具有很大殺傷力的安全漏洞。它的殺傷力大正是因爲很多開發者不知道這個漏洞。

    舉個栗子,如果你網站 A 上的「登出」功能是這樣實現的:

    <a href="http://a.com/logout.php">登出</a>

    則存在 CSRF 漏洞。假設網站 B(當然也可以是網站 A 本身)中有這麼一段代碼:

    <img src="http://a.com/logout.php">

    那麼當用戶訪問的時候,就會導致網站 A 上的會話被登出。

    需要注意的是,不只是 GET 類請求,POST 類請求同樣會存在 CSRF 漏洞,例如網站 B 中:

    <form action="http://a.com/transaction" method="POST" id="hack">
       <input type="hidden" name="to" value="hacker_account">
       <input type="hidden" name="value" value="100000">
    </form>
    <script>document.getElementById("hack").submit();</script>

    那麼用戶訪問網站 B 的時候,便會自動攜帶 A 的 SESSION 信息,成功 POST /transaction 到網站 A。

    這個漏洞危害很大,例如以前某些 BTC 交易所就存在這個漏洞,一旦用戶被誘騙訪問了黑客精心佈置的網站就會造成資金損失;又例如,以前某中國著名社交網站也存在這個漏洞,更糟糕的是該網站還允許用戶遞交自己的 <img> 顯示在所有人的信息流中,且某些傳播性操作可以用 GET 方式達成,這就導致黑客可以進行病毒式擴散,當然,僅僅是把 <img src="logout.do"> 嵌入信息流也是有足夠大殺傷力的 :)

    解決方法:

    1. 給所有請求加上 token 檢查。token 一般是隨機字符串,只需確保其不可預測性即可。token 可以在 QueryString、POST body 甚至是 Custom Header 裏,但千萬不能在 Cookies 裏。

    2. 檢查 referer (請注意,這往往不能防禦來自網站自身的 CSRF 攻擊,如用戶評論中的 <img> 就是一個常見觸發點)

後端安全

在這裏,後端安全指的是對服務器數據等可能造成直接影響的安全問題。黑客一般會直接構造數據進行攻擊。

  1. SQL 注入漏洞

    SQL 注入漏洞應該也是一個大多數開發者都知道的漏洞。

    考察以下 PHP 代碼:

    <?php
    
    $user = mysql_query('SELECT * FROM USERS WHERE UserName="'.$_GET['user'].'"');

    那麼當請求中 user 參數爲 ";DROP TABLE USERS;-- 時,合成的 SQL 語句是:

    SELECT * FROM USERS WHERE UserName="";DROP TABLE USERS;--"

    這裏產生什麼結果就不需要解釋了吧 :) 另外,通過 SQL 注入,往往還能拿到系統權限。

    解決方法:

    所有 SQL 語句都使用參數化查詢(推薦)或對參數進行 escape(不推薦)

  2. 權限控制漏洞

    這個就不仔細展開了,未經授權可以進行的操作都是權限控制漏洞。

    例如,某些網站的後臺操作就仗着「以爲用戶不知道入口地址」不進行任何權限檢查,又例如,某些操作可能出現不允許更改的字段被用戶遞交更改(往往是那些網頁上標記爲 disabled 或者 hidden 的字段),再例如,允許通過 ../ 訪問到不應該被訪問的文件等(一般存在於 include 中)。

    解決方法:

    所有地方都要進行權限檢查(如是否已登錄、當前用戶是否有足夠權限、該項是否可修改等),總之,不要相信任何來自用戶的數據,URL 當然也是。

  3. Session 和 Cookie 是兩種用於存儲用戶當前狀態的工具。某些開發者不瞭解 Session 與 Cookie 的區別,誤用或者混用,導致敏感信息泄露或者信息篡改。

    Cookie 存儲在瀏覽器上,用戶可以查看和修改 Cookie。
    Session 是存儲在服務端的數據,一般來說安全可靠;大多數 Session 都是基於 Cookie 實現的(在 Cookie 中存儲一串 SESSION_ID,在服務器上存儲該 SESSION_ID 對應的內容)。

  4. IP 地址

    首先,用戶的 IP 地址一般存儲在 REMOTE_ADDR 中,這是唯一的可信的 IP 地址數據(視不同語言而定)。然後某些代理服務器,會將用戶的真實 IP 地址附加在 header 的 VIA 或 X_FORWARDED_FOR 中(因爲REMOTE_ADDR 是代理服務器自身的 IP)。所以,要獲取用戶 IP 地址,一般做法是,判斷是否存在 VIA 或者 X_FORWARDED_FOR 頭,如果存在,則使用它們,如果不存在則使用 REMOTE_ADDR。這也是網上大多數所謂教程提供的方法。

    這就產生問題了,X_FORWARDED_FOR 或 VIA 是 HTTP Header,換句話說,它們是可以被僞造的。例如,在投票中,如果採信了 X_FORWARDED_FOR,往往意味着被刷票。

    解決方法:

    只使用 REMOTE_ADDR 作爲獲取 IP 的手段。

  5. 驗證碼

    驗證碼裏常見的問題有:非一次性、容易被識別。

    非一次性指的是,同一個驗證碼可以一直被用下去。一般來說,每進行一次驗證碼校對(無論正確與否),都應該強制更換或清除 Session 中的驗證碼。

    關於識別問題,在當前科技水平下,不加噪點不加扭曲的驗證碼幾乎是 100% 可識別的。所以大家自己看着辦吧…

後記

本文僅僅列舉了一些常見的 Web 開發安全問題,還有一些很少出現但確實有危害的安全問題沒有出現在這裏(例如,點擊劫持),讀者可以自行了解。

總之,這是一篇科普文,希望開發者可以瞭解並重視 Web 開發安全問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章