網站10大常見安全漏洞及解決方案
一般來說牛逼點的地方都會通過安全裝置來確保網路環境的安全,所以之前我們也都認為程式設計師不需要過多的考慮網站安全問題。實際上隨著做了幾個事業單位的網站之後,也逐漸發現有些方面還是需要程式設計師注意的。
1. SQL注入 安全等級★★★★★
幾乎每一個網站後臺開發人員都聽到的一個詞,並且都很敏感,但是不知道是什麼原因造成的很多程式設計師卻在實際開發過程中經常忽視這個問題。前段時間部門一位新同事,據說是5年工作經驗,在對他的程式碼做評審時,我們發現所有的DAO層實現都是直接拼接SQL和引數,總監多次提醒他這個問題,但他也沒有發現,直到總監說出SQL注入這個詞。
實際上這個漏洞很嚴重,一旦被注入成功,後果不堪設想,但這類問題處理起來還是蠻簡單的,下面以JAVA為例舉例說明
方案一: 編寫攔截器過濾請求(不推薦),此方案建議只在對維護中專案或者程式碼結構比較亂的情況下使用,底層不容易修改或者不方便修改。此方案效率低,效果也不佳。
/** * 校驗引數,判斷是否包含sql關鍵字表達式 * 此方法會有誤傷 */ private static boolean sqlValidate(String str) { str = str.toLowerCase();//統一轉為小寫 String badSqlStr = "'|exec|execute|insert|select|delete|update|count|drop|chr|mid|master|truncate|char|declare|sitename|net user|xp_cmdshell|create|drop|" +"table|from|grant|use|group_concat|column_name|1=1|=|" +"information_schema.columns|table_schema|union|where|*|" +"chr|mid|master|truncate|char|declare|or|;|,|like|//|/|%|#|-|--|+|"; String badXssStr = "frame|iframe|script|javascript|=|<|>"; //sql 關鍵詞 有空格分隔 String[] badSqlStrs = badSqlStr.split("\\|"); for (int i = 0; i < badSqlStrs.length; i++) { if (str.indexOf(" "+badSqlStrs[i]) >= 0 || str.indexOf(badSqlStrs[i]+" ") >= 0) { return true; } } } return false; }
方案二:推薦方案,使用預編譯的prepareStatement代替statement;使用框架中的setParameter設定引數,此方案可有效處理SQL注入問題。
@Override public List<NewsColumn> getColumnListByType(String columnType) { String jql = " FROM "+NewsColumn.class.getName(,)+" WHERE type=:type "; List<NewsColumn> list = entityManager.createQuery(jql).setParameter("type", columnType).getResultList(); return list; }
2. 驗證碼必須後臺校驗 安全等級★★★★
前段時間一客戶說後臺管理看到了一堆這樣使用者,/etc/init.d /1=1 ./././ …. Windows/ ,本該截圖的,後來處理了就給忘了~~~~,反正從註冊內容來看,可以確定兩點,通過註冊機註冊,想通過註冊注入攻擊。
通過機器註冊:直接跳過了前端的表單校驗,而恰巧這個專案在開發的時候,驗證碼只在前端做了校驗,提交到後臺沒有做再一次的校驗,也就是這個漏洞導致了這堆垃圾註冊。
解決方案:前臺提交資料到後臺後做進一步校驗,如驗證碼校驗、資料格式校驗、驗重校驗。
回想我也曾經用過這個漏洞…老東家海航集團2014年的時候,OA系統添加了登入需要手機簡訊驗證,當時系統更新後第一個版本就是僅做了前臺校驗,這個漏洞無意中被我發現了,因為每天都要登入OA,每次都要簡訊實在太麻煩了,我就嘗試模擬了個表單請求,重寫原登入系統表單提交的指令碼,在所有的驗證我都直接返回true。很激動的是,一次就成功了,後來也分享給我們同事了,大家都很開心。但隨後不久,系統就升級了,後臺驗證,你們懂得。當然對當時的資訊部同事來說,我就是一個壞人……
3. 防止表單重複提交 安全等級★★★
防止表單重複提交其實網上有很多解決方案,並且現在主流的前端框架都可以在頁面上做按鈕控制,不過做為一個程式設計師,你們懂得,這並沒有什麼卵用。個人還是建議採用實際的後臺驗證法處理。從網上爬文,看到的靠譜的解決方案如下。
解決方案:token驗證,請求頁面時生成token並放在session中,提交表單到後臺驗證token,業務邏輯處理完之後,清除token。如果表單提交了一次,token就沒了,再次提交就無法通過了。
方案分析:此方法和驗證碼基本上一致,如果驗證碼在每次表單提交後都清除一次,也能達到這樣的效果。
其他建議:重要的表單頁面提交後重定向,取消表單的autocomplete。
4. 檔案上傳格式校驗 安全等級★★★★
黑客攻擊網站還有一個常見的方式就是通過檔案上傳漏洞,比如網站上傳圖片的功能沒有嚴格校驗字尾名。黑客可以通過此功能上傳一些指令碼檔案,上傳成功後,通過請求這些指令碼檔案執行指令碼中的功能達到攻擊的目的。
那麼如果驗證了上傳檔案的字尾名就可以嗎?實際上並不是,舉例說我們知道頁面引入script標籤時src寫啥都行,比如http://www.baidu.com/123.jpg,也是可以的,攻擊者只需要把一個script檔案字尾名改為jpg即可通過後綴驗證,後面一路暢通。所以這就提到了驗證檔案的真實格式。如何驗證,網上一大堆…
解決方案:設定php檔案、jsp檔案不可直接被訪問(不知道php可以不,jsp放在WEB-INF即可),這樣攻擊者上傳此類檔案也無法執行;通過檔案頭資訊嚴格驗證檔案格式,從上傳功能開始防範。
5. 熟悉使用框架或資料庫版本情況 安全等級★★
實際開發中,我們都使用一些開源框架,但這些框架也不是百分百完善的,比如webwork,struts2就經常爆出一些漏洞,這個需要開發者自行關注。
解決方案:根據官方釋出的方案進行版本升級;根據公開的漏洞執行方式編寫攔截器。
Struts2 s2-016 漏洞處理例項(專案結構不允許版本升級),攔截器,實際上官方的版本也是升級後過濾了一些引數。
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse)response;
//禁止頁面被frame
res.addHeader("x-frame-options","SAMEORIGIN");
Map parameterMap = req.getParameterMap();
for (Iterator iterator = parameterMap.keySet().iterator(); iterator.hasNext();) {
String key = (String) iterator.next();
if ((key.contains("redirect:")) || (key.contains("redirectAction:")) || (key.contains("action:"))) {
res.sendError(HttpServletResponse.SC_FORBIDDEN, "forbidden");
System.out.println("----------非法操作-----------");
return;
}
}
filterChain.doFilter(request, response);
}
6. 嚴禁在生產環境下使用預設密碼 安全等級★★
很多時候一些小網站,新人練手的網站(往往價格很便宜),在開發的過程中都要求很快,往往這些網站問題還是蠻多的。後臺驗證碼處於關閉狀態、賬號密碼常用admin/admin組合,看似方便了操作,實際是危險重重。甚至有時候,資料庫連結密碼都是root/空,這個危害大家都知道。由於沒有驗證碼,使用者密碼又使用的預設的,黑客爆破的概率異常的高,一旦獲取了後臺管理許可權,剩下的就交給你了。
解決方案:通過配置測試模式和生產模式控制驗證碼驗證,管理員賬戶必須使用非預設加密處理,必要時使用物理驗證。
7. 伺服器埠儘可能少開 安全等級★★★
六月份一個客戶的伺服器被掛馬了,伺服器一直是他們自行維護,只在專案更新時給我們開放遠端。無意中發現伺服器防火牆竟然是關閉狀態~~~~我的天吶,是不是所有埠都處於開放狀態吶~~~~你們以為這就是驚喜,驚喜還在後面呢,客戶提供的MySQL資料庫使用者名稱明碼是root root,個人認為這個才是驚喜。root/root和root/空是一樣的效果。分析了一下,防火牆關閉了,3306也就開了,黑客發現3306開著,root/root能連線,通過windows漏洞提升root使用者為系統使用者,bingo,竟然可以登入這臺伺服器欸,簡直棒棒噠。
解決方案:防護牆開啟,僅開放必要的埠如80,13389,設定遠端登入IP白名單。再次強調不要用預設賬戶。
8. Options方法過濾 安全等級★
這個問題網上提到的都是很嚴重,但現在並沒有發現多少案例,或許處理方案比較簡單吧。
解決方案:在web.xml新增配置
<!-- 過濾不安全的請求方法 -->
<security-constraint>
<web-resource-collection>
<url-pattern>/*</url-pattern>
<http-method>PUT</http-method>
<http-method>DELETE</http-method>
<http-method>HEAD</http-method>
<http-method>OPTIONS</http-method>
<http-method>TRACE</http-method>
</web-resource-collection>
<auth-constraint>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
9. XSS攻擊、CSRF攻擊 安全等級★
XSSS攻擊處理方案一般來說也是通過攔截器過濾請求引數,都是常規的處理方案。
package com.interceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* <p>
* 類說明
* </p>
* @author shy
* @date 2016-4-21 下午03:34:26
* @vesion $Revision$ $Date$
*/
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest = null;
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
/**
* 覆蓋getParameter方法,將引數名和引數值都做xss過濾。<br/>
* 如果需要獲得原始的值,則通過super.getParameterValues(name)來獲取<br/>
* getParameterNames,getParameterValues和getParameterMap也可能需要覆蓋
*/
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 覆蓋getHeader方法,將引數名和引數值都做xss過濾。<br/>
* 如果需要獲得原始的值,則通過super.getHeaders(name)來獲取<br/>
* getHeaderNames 也可能需要覆蓋
*/
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (value != null) {
value = xssEncode(value);
}
return value;
}
/**
* 將容易引起xss漏洞的半形字元直接替換成全形字元
*
* @param s
* @return
*/
private static String xssEncode(String s) {
if (s == null || "".equals(s)) {
return s;
}
StringBuilder sb = new StringBuilder(s.length() + 16);
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
switch (c) {
case '>':
sb.append('>');//全形大於號
break;
case '<':
sb.append('<');//全形小於號
break;
case '\'':
sb.append('‘');//全形單引號
break;
case '\"':
sb.append('“');//全形雙引號
break;
case '&':
sb.append('&');//全形
break;
case '\\':
sb.append('\');//全形斜線
break;
case '#':
sb.append('#');//全形井號
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
/**
* 獲取最原始的request
*
* @return
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 獲取最原始的request的靜態方法
*
* @return
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) req).getOrgRequest();
}
return req;
}
}
CSRF攻擊(這個還在學習中)
解決方案:從網上爬文看到的基本上是一致的,校驗Referer,新增請求token驗證,個人覺得此漏洞在大型系統中比較重視,小網站就呵呵了。
10. frame引入控制 安全等級★
這個不知道為什麼會被列入網站安全問題中,一個客戶網站找了第三方安全檢測公司檢測網站有這個漏洞,所以就不得不處理。網上抄來的。
Java程式碼(攔截器中使用):
response.addHeader("x-frame-options","SAMEORIGIN");
Nginx配置:
add_header X-Frame-Options SAMEORIGIN
Apache配置:
Header always append X-Frame-Options SAMEORIGIN
為毛沒有tomcat的配置方式呢???
相關資料: