高級java開發工程師也會犯得錯誤,你中槍了嗎。

高級java開發工程師也會犯得錯誤,你中槍了嗎。

寫在前面的話:記錄一下平常開發造成項目安全問題的漏洞,以免自己今後再犯錯誤。

1.SQL注入

SQL注入攻擊即用戶通過輸入非法字符串,篡改原SQL語句的意圖。SQL語句由用戶輸入條件動態組裝,就存在此風險。
例如1. 屏蔽查詢條件:

 String userName = ctx.getAuthenticatedUserName();
 String itemName = request.getParameter("itemName");
 String query = "SELECT * FROM items WHERE owner = '" 
    + userName + "' AND itemname = '"  
    + itemName + "'";
 ResultSet rs = stmt.execute(query);

如果輸入的條件中爲恆等式,就可能導致信息泄露,例如如果用戶輸入的userName參數值爲“Juice’ OR ‘a’=‘a”,那麼該查詢語句的WHERE字句就失去了條件過濾的功能。
例如2. 篡改原SQL語句意圖:
上面的例子中如果輸入的userName參數值爲“name’; DELETE FROM items; --”,那麼原查詢語句就會變成一條查詢語句與一條刪除操作語句,將導致在該查詢動作之後,執行delete,刪除items表中所有數據。

爲避免SQL注入攻擊,我們一般可以通過兩種方式避免:

  1. 避免SQL語句的組裝,使用“?”佔位符的預編譯SQL語句;
    …//使用jdbc時
    Connection conn = getConn();//獲得連接
    String sql = “select id, username, password, role from user where id=?”; //執行sql前會預編譯號該條語句,無論id輸入什麼字符都會被當做文本來處理
    PreparedStatement pstmt = conn.prepareStatement(sql);
    pstmt.setString(1, id);
    ResultSet rs=pstmt.executeUpdate();

    …//使用mybatis框架時

    select id, username, password, role from user where id=#{}

  2. 如果無法避免SQL語句的組裝,則應該對用戶輸入參數進行特殊字符處理。例如:
    String id = request.getParameter(“id”);
    id=id.replaceAll(“’”,”’’”)//將id中的單引號’替換成雙引號’’
    id=id.replaceAll(“;”,””)//將id中的分號;去除
    String query = "SELECT * FROM user WHERE id =”+id

2. 資源未及時釋放

系統中的一些關鍵資源是非常有限的,必須有效管理和使用才能保證系統的性能和穩定性,例如文件資源,網絡連接資源,數據庫連接資源,同步鎖資源等。
資源釋放需要徹底,否則可能出現內存泄露或資源無法回收的情況,例如在執行數據庫操作時,我們首先獲得數據庫的Connection對象,然後基於此對象創建如Statement對象等數據庫操作對象,那麼在釋放資源時,應該按創建對象的逆序關閉Statement對象和Connection對象,確保資源完全釋放。
下圖所示爲出現該風險的問題代碼:
在這裏插入圖片描述
inS爲InputStream, 在finnally中對InputStream進行close時,本身可能會觸發IOExceptions。 而一旦觸發IOExceiponts,則之後的out(out爲FileOutputStream)流將無法被成功釋放資源。因此Foritfy報Unreleased Resource:Streams風險,提示可能無法正常釋放FileOutputStream資源。
解決辦法:
在這裏插入圖片描述
在這裏插入圖片描述
以上方案使用了一個助手函數safeClose,用以記錄在嘗試關閉fis流時可能發生的異常,catch住關閉流時可能引起的異常,也使後續的out流能夠不會因爲前面關閉流時出現異常而無法被執行到,導致後續的out流無法釋放資源。

3. 空指針檢查

空指針一般出現在程序員對對象的一個或多個假設前提下。如果程序明確將對象設置爲 null,但稍後卻間接引用該對象,則將出現空指針異常。
大部分空指針問題只會引起一般的軟件可靠性問題,但如果攻擊者能夠故意觸發空指針間接引用,攻擊者就有可能利用引發的異常繞過安全邏輯,或致使應用程序泄漏調試信息,這些信息對於規劃隨後的攻擊十分有用。所以在使用對象前,應該做非空檢查,避免出現空指針異常。
問題舉例:
Foo foo = null;
………………
foo=new Foo(parameters)
foo.setBar(var);
…….
以上代碼中,new方法有可能失敗,程序員間接引用foo的時候,foo有可能爲null。故需要對foo進行檢查。
解決辦法:
Foo foo = null;
………………
if (foo != null){
foo.setBar(var);
}
…….
在所有初始化賦值爲NULL的地方,正式引用前都需要做非空檢查。

4. 文件分隔符

不同的操作系統使用不同的字符作爲文件分隔符。 例如,Microsoft Windows 系統使用 “”,而 UNIX 系統則使用 “/”。 應用程序需要在不同的平臺上運行時,使用硬編碼文件分隔符會導致應用程序邏輯執行錯誤。
解決辦法:
建議使用“File.separator”獲得文件分隔符,避免硬編碼。

5.跨站腳本攻擊

產生場景:
1外部輸入的數據未驗證而直接頁面輸入
2數據庫讀取的數據未驗證直接頁面輸入
例如,以下代碼可從 HTTP servlet 請求中讀取僱員 ID eid,並在 servlet 響應中將值顯示給該用戶。

String eid = request.getParameter("eid");
ServletOutputStream out = response.getOutputStream();
out.print("Employee ID: " + eid);
out.close();

如果 eid 只包含標準的字母或數字文本,這個例子中的代碼就能正確運行。如果 eid 裏有包含元字符或源代碼中的值,那麼 Web 瀏覽器就會像顯示 HTTP 響應那樣執行代碼。
起初,這個例子似乎是不會輕易遭受攻擊的。畢竟,有誰會輸入導致惡意代碼的 URL,並且還在自己的電腦上運行呢?真正的危險在於攻擊者會創建惡意的 URL,然後採用電子郵件或者社會工程的欺騙手段誘使受害者訪問此 URL 的鏈接。當受害者單擊這個鏈接時,他們不知不覺地通過易受攻擊的網絡應用程序,將惡意內容帶到了自己的電腦中。這種對易受攻擊的 Web 應用程序進行盜取的機制通常被稱爲反射式 XSS。
針對漏洞產生的原因,解決方案如下:

  1. 在數據流出應用程序(輸出驗證)的前一刻對其進行驗證。
  2. 加強一個應用程序現有的輸入驗證機制。
  3. 最安全的驗證方式:創建一份安全字符白名單,允許其中的字符出現在HTTP內容中,只接受完全由這些經認可的字符組成的輸入。
  4. 最靈活的驗證方式:黑名單方法(對於web瀏覽器具有特殊含義的字符集,如“<”、“&”、“>”、“;”、“/”、“:”=”、“+”、“?”、“%”、“”等)這種方法在進行輸入之前就選擇性地拒絕或者避免了潛在的危險字符。
    例如:
    例:提供公共方法,結合實際業務需求,對輸入源和輸出源調用該方法進行特殊字符的過濾,主要是瀏覽器腳本可能包含的一些特殊字符。如圖
    在這裏插入圖片描述
    在spring框架中,平臺的所有controller都繼承自基類BaseController,該類通過@InitBinder對返回值對特殊字符進行處理,從而避免XSS攻擊。
/**
	 * 初始化數據綁定
	 * 1. 將所有傳遞進來的String進行HTML編碼,防止XSS攻擊
	 * 2. 將字段中Date類型轉換爲String類型
	 */
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		// String類型轉換,將所有傳遞進來的String進行HTML編碼,防止XSS攻擊
		binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value != null ? value.toString() : "";
			}
		});
		// Date 類型轉換
		binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(DateUtils.parseDate(text));
			}
		});
	}

6.僞造日誌攻擊

日誌是對系統行爲的歷史記錄,作爲系統排錯和自動報警等的基礎信息,如果如果日誌輸出內容包括用戶輸入信息,則存在僞造日誌攻擊的風險。
例如1:
攻擊者提交字符串 “twenty-one%0a%0aINFO:+User+logged+out%3dbadguy”,並且該字符串被作爲日誌信息直接寫到日誌中,則日誌中會記錄以下條目:
INFO: Failed to parse val=twenty-one
INFO: User logged out=badguy
日誌信息中就多了一條“INFO: User logged out=badguy”的日誌。

通過這種方式,用戶通過控制輸入信息,僞造日誌信息。如果系統的輸出日誌,將被自動處理、自動信息報警等,惡意用戶可以通過該方式破壞日誌文件,或者提供誤報信息等;用戶甚至可以篡改日誌,隱藏惡意操作的痕跡,或者插入惡意代碼,在日誌文件被解析時執行。
解決辦法:
對用戶輸入信息都需要進行特殊字符過濾再寫入日誌文件中,如’%0d’,’\r’,’\n’,’0a’

7.XML實體注入

XML注入攻擊即如果攻擊者能夠寫入原始 XML,則可以更改 XML 文檔和消息的語義。 危害最輕的情況下,攻擊者可能插入無關的標籤,導致 XML 解析器拋出異常。更爲嚴重的情況下,攻擊者可以添加 XML 元素,更改 authentication 憑證或修改 XML 電子商務數據庫中的價格。 還有些情況,XML injection 甚至可以導致 Cross-Site Scripting 或 Dynamic Code Evaluation。
例如1:
假設攻擊者能夠控制下列 XML 中的 shoes。

<order>
   <price>100.00</price>
   <item>shoes</item>
</order>

如果攻擊者能夠寫入原始 XML,將XML文件內容修改爲:

<order>
	<price>100.00</price>
	<item>shoes</item>
	<price>1.00</price>
	<item>shoes</item>
</order>

在標籤中用戶通過輸入重複的price標籤值和item標籤值,覆蓋前面的值,從而篡改了商品價格。
對xml實體進行解析式,使用安全配置的實例:
1.如果使用XML Factory時,必須配置如下屬性:

factory.setFeature("http://xml.org/sax/features/external-general-entities", false);
factory.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

2.如果不需要內嵌文本形式的xml,可以使用如下屬性:

factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);

3.如果使用的是SAX解析,可以使用如下配置:

xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false);
xmlInputFactory.setProperty("javax.xml.stream.supportDTD", false);

8.路徑操縱攻擊(Path Manipulation)

路徑操縱即用戶通過控制文件訪問路徑而進行非法訪問或越權訪問文件。例如:
String rName = request.getParameter(“reportName”);
File rFile = new File("/usr/local/apfr/reports/" + rName);

rFile.delete();
本段代碼是期望用戶在指定的目錄下面選擇刪除自己希望刪除的文件,並且系統對該操作時允許的。不過如果輸入參數並不是系統期望的文件名,而是類似這樣的輸入:“…/…/tomcat/conf/server.xml”,從而導致應用程序刪除它自己的配置文件。
解決辦法:
1) 黑名單檢查:檢查路徑中不能包含“…”以及“:”
2) 白名單檢查:如檢查文件類型,只允許特定後綴的文件名。

9.HTTP報文頭控制攻擊

HTTP 響應頭文件中包含當前請求的響應狀態,響應內容等,如果其中包含用戶輸入的未經驗證的數據,那麼可能會引發 cache-poisoning、cross-site scripting、cross-user defacement、page hijacking、cookie manipulation 或 open redirect風險。
例如:
Cookie cookie = new Cookie(“author”, author);
cookie.setMaxAge(cookieExpiration);
response.addCookie(cookie);
如果攻擊者提交的是一個惡意字符串,比如 “Wiley Hacker\r\nHTTP/1.1 200 OK\r\n…”,那麼 HTTP 響應就會被分割成以下形式的兩個響應:
HTTP/1.1 200 OK

Set-Cookie: author=Wiley Hacker
HTTP/1.1 200 OK

實際請求響應頭會被用戶輸入的響應頭覆蓋,達到欺騙客戶端瀏覽器的作用,從而導致瀏覽器作出一些錯誤的有利於攻擊者的操作,例如重定向等。
解決辦法:
對所有涉及到用戶輸入的報文頭參數都需要進行特殊字符過濾,如’\r’,’\n’

10.僞隨機碼

標準的java.util下的僞隨機數生成器不能抵擋各種加密攻擊。
在對安全性要求較高的環境中,使用一個能產生可預測數值的函數作爲隨機數據源,會產生不安全的隨機數錯誤。
修改辦法:
建議使用java.security.SecureRandom提供的方法來替代僞隨機數生成方法。

11.隱私信息管理

系統應該對用戶隱私信息做有效的管理和保護,不被盜取和破壞。如果對各種隱私信息處理不當,如客戶密碼或重要憑證號碼等,會危及到用戶的個人隱私,這是一種非法行爲。例如:
………
UserInfo userInfo = new UserInfo();
String password=userInfo.getPassword();
log.info(“密碼”+password)
………

爲了有效管理用戶的隱私信息,系統應遵循以下規則:

  1. 不必要的用戶私人信息不要進入程序;
  2. 不要將用戶的隱私數據寫到外部介質,例如控制檯、日誌 ,不能以明文的形式傳輸用戶的隱私信息。

12.命令注入攻擊

命令注入攻擊一般分兩種途徑:

  1. 通過修改程序所用的環境變量值,將待執行的命令指向另一個位置的同名命令;
  2. 控制待執行的命令,例如Java系統函數System.Runtime.getRuntime().exec(cmd)執行的字符串指令“cmd”中可以通過“&&”分割多條指令,並依次被執行。如果這個指令字符串有用戶輸入部分,則用戶可以通過“&&”加入其他命令。
    例如1:

    String home = System.getProperty(“APPHOME”);
    String cmd = home + INITCMD;
    java.lang.Runtime.getRuntime().exec(cmd);

    本段代碼根據系統屬性APPHOME來決定命令所在目錄,如果該系統屬性能被攻擊者控制,那麼被執行的命令也可能不是期望的命令。
    例如2:

    String btype = request.getParameter(“backuptype”);
    String cmd = new String(“cmd.exe /K “c:\util\rmanDB.bat “+btype+”&&c:\utl\cleanup.bat””)
    System.Runtime.getRuntime().exec(cmd);

    本段代碼接受用戶輸入的參數“backuptype”,但是沒有做任何檢查。命令中通過“&&”告訴“cmd.exe”順序執行rmanDB.bat和cleanup.bat命令,如果用戶輸入的參數中包括以“&&”分割的別的命令,例如“&& del c:\dbms\.”,那麼這個刪除命令也會被執行。所以,爲了避免命令注入攻擊,我們應該對待執行命令的有效性做檢查,包括命令路徑和命令文件是否合法,以及執行的命令參數是否合法。
    修改辦法:
    對輸入的命令進行校驗,如果輸入的命令中含有命令分隔符,則定性爲不安全的命令,則不予執行。比如命令中含有 “&&” “ ||” “ ;”

13.密碼明文存儲或硬編碼

將明文密碼直接寫在配置文中會降低系統的安全性。例如將數據庫帳號密碼直接未經加密填寫在properties文件:
jdbc.username=root
jdbc.password=root
修改辦法:
將明文密碼通過可逆算法加密後寫入配置文件,如異或,base64算法,例如base64加密:
jdbc.username=root
jdbc.password=cm9vdA==
程序讀密碼後再進行解密使用,bases64需要依賴sun公司提供的jar包sun.misc.BASE64Encoder

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