軟件開發過程中安全代碼的七大實踐
譯文【51CTO.com快譯】眾所周知,軟件的安全性如今已得到了前所未有的重視程度。許多企業會將安全性嵌入到應用程序的開發階段。這樣既能有利于整體安全性的遵守,又可以在軟件的不同層面上創建多個安全性檢查點。本文將通過如下圖所示的各種方面,以實例的形式,向您展示各種安全代碼的實踐。雖然主要是以Java為例,但是它們也可以被運用到任何其他編程語言上。
1.轉義/逃逸輸入(Escape the Input)
所謂轉義攻擊是指攻擊者將執行命令/查詢,偽裝并嵌入到普通的文字輸入中,通過欺騙應用程序的執行引擎,而讓其能夠向攻擊者提供各種信息與控制權??梢?,為避免此類攻擊的發生,我們需要對用戶的輸入進行轉義,將其解釋為文字,而非某些命令。同理,我們也需要對存儲在數據庫中的數據進行轉義。
試想,如果某用戶在其回帖的文字輸入中帶有JavaScript,那么他就可以試圖從瀏覽器中竊取到Cookie。例如,當該回帖的內容被呈現在其他用戶的瀏覽器屏幕上時,一旦我們的程序代碼不去轉義帖子中的包含的惡意代碼。那么該JavaScript代碼將被執行,并為攻擊者提取各種所需的信息與控制權。以下是帶有潛在風險的數據庫查詢代碼,和相應的采取了轉義措施的Java代碼。
示例:
包含潛在風險的Java代碼
- String query = "SELECT user_id FROM user_data WHERE user_name = '"
- + req.getParameter("userID")
- + "' and user_password = '" + req.getParameter("pwd") +"'";
- try {
- Statement statement = connection.createStatement( … );
- ResultSet results = statement.executeQuery( query );
- }
安全的Java代碼
- Codec ORACLE_CODEC = new OracleCodec();
- String query = "SELECT user_id FROM user_data WHERE user_name = '"
- + ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("userID"))
- + "' and user_password = '"
- + ESAPI.encoder().encodeForSQL( ORACLE_CODEC, req.getParameter("pwd")) +"'";
2.避免將ID作為序列號
在某些情況下,攻擊者會設法超過現有的限制,以獲取更多的信息。例如,某個API的用戶只被允許查看ID號為1-100的用戶信息。而如果該系統采用的是以ID為順序的遞增編號方式,那么我們就可以預測到下一個用戶的序列號將是101。由此,攻擊者便可以利用這一邏輯上的漏洞,來獲取在其權限之外的信息。
示例:
包含潛在風險的Java代碼
- String sqlIdentifier = "select TESTING_SEQ.NEXTVAL from dual";
- PreparedStatement pst = conn.prepareStatement(sqlIdentifier);
- synchronized( this ) {
- ResultSet rs = pst.executeQuery();
- if(rs.next())
- long myId = rs.getLong(1);
安全的Java代碼
- // This example is for Oracle
- String sqlIdentifier = "select TESTING_SEQ.NEXTVAL from dual";
- PreparedStatement pst = conn.prepareStatement(sqlIdentifier);
- synchronized( this ) {
- ResultSet rs = pst.executeQuery();
- if(rs.next())
- long myId = rs.getLong(1) + UUID.random();
3.運用極簡主義方法
為了減少攻擊面,系統應采用最小的空間使用策略。從本質上說,這就意味著系統能夠很好地避免各種權限的暴露。例如,根據某項業務需求,系統需要使用代碼“HTTP 200”,來響應存在著被請求的資源。但是如果我們為REST API提供了get操作,那么就會增加攻擊者的攻擊面。相反,該系統應該只通過HTTP協議的head方法,來提供有關現有資源的信息,而不必提供更多的無關信息。
示例:
包含潛在風險的Java代碼
- //Get is allowed where we need to just check user exist
- http://localhost:8080/User/id/1
安全的Java代碼
- http://localhost:8080/User/id/1
- Head
4.最小特權原則
讓我們試想一個場景:通常,客服部門某個用戶的常規訪問權限是可以訪問訂單數據的API。但是為了簡便起見或是某種原因,系統為其分配了超級管理員的角色。那么一旦他所處的系統被黑或遭到了帳號破壞,攻擊者就可以利用他的超級管理員權限,來對該系統發起一系列的攻擊操作??梢?,為了減少攻擊面,我們應當僅根據實際需求,以及既定的角色,來授予目標API相應的最小訪問權限,不應該在系統中設置所謂可以訪問所有內容的超級用戶角色。
5.盡可能使用HTTPS或雙向SSL
切勿以最原始的HTTP方式發布您的網站或是節點。畢竟如今大多數瀏覽器都會對那些單純的HTTP站點顯示警告。而且,業界建議針對集成的端點采用雙向(2-Way)SSL方式,而對網站或站點通過HTTPS的方式,實現端到端加密。
不過,由于HTTPS只能保護了通信信道免受攻擊,卻無法在通道的密鑰發生泄露時,保護數據。因此,業界建議使用強大的加密算法,對各種數據記錄先進行加密,再通過可信的網絡予以傳輸。
6.不要使用不安全的或弱的加密算法
如今,隨著計算機算力的不斷迭代與提高,弱的密鑰已不再能夠防止那些暴力破解的攻擊手段。一些知名組織甚至將如下不安全的、或弱的加密算法,列入了所謂的“黑名單”。因此您在日常進行安全編程時,應當盡量避免使用到它們。
- SHA-1
- 1024位RSA或DSA
- 160位ECDSA(橢圓曲線)
- 80/112位2TDEA(雙密鑰三重DES)
- 與其他各種舊算法類似,MD5從來都不是政府可以接受的算法。
7.將動態可執行代碼(Dynamically Executed Code)列入白名單
如果您有一些代碼是從API或APP的用戶側傳入的,或者是在用戶輸入之后才生成的,那么為了讓它們能夠作為整體流程的一部分被執行,您需要讓系統將這些待執行的命令列入白名單。例如,如果系統需要公布某項服務,以列出服務器上的對應目錄,那么我們就需要將ls或dir之類的命令列入白名單,并轉義用戶輸入的標志。
小結
綜上所述,我們從加密、編碼、白名單、最小特權、以及轉義不可信的用戶輸入等方面,為您羅列了日常軟件開發過程中的七種安全編碼的實踐示例。希望它們能夠協助您大幅減少軟件所面臨的各種安全威脅,并提高自身的代碼級安全態勢。
原文標題:7 Practical Secure Coding Practices,作者:Awkash Agrawal
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】