就以前本人主持開發的金融產品所遇到的安全問題,設計部分請參見:http://www.cnblogs.com/shenliang123/p/3835072.htmljavascript
這裏就部分web安全防禦就簡單的交流:html
(1)跨站腳本攻擊(XSS):java
XSS攻擊,一般指黑客經過「html注入」 篡改了網頁,插入了惡意的腳本,從而在用戶瀏覽網頁的時候,控制用戶瀏覽器的一種攻擊。web
最多見的XSS攻擊就是經過讀取瀏覽器的Cookie對象,從而發起「cookie劫持」,當前用戶的登陸憑證存儲於服務器的session中,而在瀏覽器中是以cookie的形式進行存儲的,cookie被劫持後,意味着攻擊者能夠不經過密碼而直接登陸系統。咱們也能夠直接在瀏覽器中輸入腳本javascript:alert(document.cookie)來獲取當前cookie值。ajax
目前防止「cookie劫持」的方法大體有:a. 輸入檢查,使用filter來過濾敏感的關鍵字;b. 將cookie與用戶ip地址進行綁定;c. 爲cookie植入HttpOnly標識。sql
本系統採用的是第3種方式:爲cookie植入HttpOnly標識。一旦這個HttpOnly被設置,你在瀏覽器的 document對象中就看不到Cookie了,而瀏覽器在瀏覽的時候不受任何影響,由於Cookie會被放在瀏覽器頭中發送出去(包括ajax的時 候),應用程序也通常不會在js裏操做這些敏感Cookie的,對於一些敏感的Cookie咱們採用HttpOnly,對於一些須要在應用程序中用js操做的cookie咱們就不予設置,這樣就保障了Cookie信息的安全也保證了應用。數據庫
具體代碼實現:在web服務器tomcat的配置文件server.xml中添加:apache
<Context docBase="E:\tomcat\apache-tomcat-6.0.24/webapps/netcredit" path="/netcredit" reloadable="false" useHttpOnly="true"/>
或者在項目的web.xml 配置以下:數組
<session-config> <cookie-config> <http-only>true</http-only> </cookie-config> </session-config>
圖1-1 瀏覽器cookie截圖瀏覽器
(2)跨站點請求僞造(CSRF):
本系統採用了對抗CSRF最有效的防護方法:驗證碼。
服務器端的安全相對於客戶端來講顯的更加的重要,由於服務器端的某個安全漏洞可能帶來的是致命的危險。所以咱們對服務器端的安全更爲的慎重和重視。
(1)SQL注入攻擊:
Sql注入的的兩個關鍵條件:第一個是用戶可以控制輸入;第二個是本來程序要執行的代碼,拼接了用戶輸入的數據。
根據上面兩個關鍵條件,系統爲防止sql注入使用瞭如下方法:
第一:使用預編譯語句,這也是防護sql注入最有效的方法,徹底摒棄代碼的直接拼接所帶來的危險。
public List<T> findByPage(final String hql, final Object[] values, final int offset, final int pageSize) { List<T> list = getHibernateTemplate().executeFind(new HibernateCallback(){ public Object doInHibernate(Session session) throws HibernateException, SQLException{ Query query=session.createQuery(hql); for (int i = 0 ; i < values.length ; i++){ query.setParameter( i, values[i]); } if(!(offset==0 && pageSize==0)){ query.setFirstResult(offset).setMaxResults(pageSize); } List<T> result = query.list(); return result; } }); return list; }
第二:關閉web服務器的錯誤回顯功能,這樣能夠防止攻擊者對系統進行攻擊後,經過回顯的詳細錯誤信息對攻擊內容進行調整,對攻擊者提供極大的便利。咱們在項目的web.xml文件中添加如下示例代碼:
<error-page> <error-code>400</error-code> <location>/error400.jsp</location> </error-page>
第三:數據庫自身使用最小權限原則,系統程序不使用最高權限的root對數據庫進行鏈接,而是使用能知足系統需求的最小權限帳戶進行數據庫鏈接,並且多個數據庫之間使用不一樣的帳戶,保證每一個數據庫都有獨立對應的帳戶。
(2)文件上傳漏洞:
文件上傳漏洞是指用戶上傳了一個可執行的腳本文件,並經過腳本文件得到了執行服務器端命令的能力,這樣將會致使嚴重的後果。而本系統內涉及到大量的圖片格式文件上傳,所以對於上傳問題的處理很是謹慎,並儘量的達到安全標準。
本系統主要經過對上傳文件詳細的格式驗證:
第一步:經過後綴名來簡單判斷文件的格式。
public static boolean isPicture(String pInput) throws Exception{ // 得到文件後綴名 String tmpName = pInput.substring(pInput.lastIndexOf(".") + 1, pInput.length()); // 聲明圖片後綴名數組 String imgeArray [] = { "jpg", "png", "jpeg" }; // 遍歷名稱數組 for(int i = 0; i<imgeArray.length;i++){ // 判斷符合所有類型的場合 if(imgeArray [i].equals(tmpName.toLowerCase())){ return true; } } return false; }
第二步:經過讀取文件的前兩個字符進行對比,例如png格式圖片的前兩個字符爲8950,而jpg格式的圖片前兩個字符爲ffd8。
public static String bytesToHexString(byte[] src) { StringBuilder stringBuilder = new StringBuilder(); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF;//byte to int String hv = Integer.toHexString(v); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } return stringBuilder.toString(); }
第三步:若是上傳的爲圖片,則獲取相應的高度和寬度,若是存在相應的寬度和高度則可認爲上傳的是圖片。
public static boolean isImage(File imageFile) { if (!imageFile.exists()) { return false; } Image img = null; try { img = ImageIO.read(imageFile); if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) { return false; } return true; } catch (Exception e) { return false; } finally { img = null; } }
若是以上驗證都成功經過,本系統在對文件進行存儲時會將文件名進行重命名處理,而且設置相應的web服務器,默認不顯示目錄。由於文件上傳若是須要執行代碼,則須要用戶可以訪問到這個文件,所以使用隨機數改寫了文件名,將極大的增長攻擊的成本,甚至根本沒法成功實施攻擊。
//對文件的名稱進行重命名 StringBuilder sb = new StringBuilder().append(new Date().getTime()).append(".").append(fileMsg[1]);
(3)認證與會話管理:
認證明際上就是一個驗證憑證的過程,所以咱們對登陸密碼有着嚴格的規定:密碼要求長度在8位以上而且包含字母,數字,符號兩種以上的組合。並將密碼加密後再進行數據庫的存儲。爲防止一些暴力破解手段,又出於用戶體驗的考慮,本系統採用用戶登陸時默認不進行驗證碼的輸入,但密碼三次輸入失敗後須要進行驗證碼的輸入,連續五次輸入錯誤後,該用戶的ip地址將被封,能夠在一段時間後自動解封或者後臺管理員手動解封。涉及到系統資金有關的操做,例如投標,咱們還須要用戶設置並提供支付密碼,而提現等操做咱們還配備短信驗證碼功能。以此來加強驗證的可靠性。
當用戶登陸完成後,在服務器端會建立一個新的會話(Session),會話中保存着用戶的狀態和相關信息,服務器端維護全部在線用戶的Session,而瀏覽器則是將SessionId加密後保存在cookie中,這裏就出現了前面提到了「cookie劫持「問題,本系統經過將cookie中植入HttpOnly成功解決該問題。本系統還給Session設置了一個有效時間,來保證在有效時間後Session將自動銷燬,以防止Session長鏈接所帶來的安全隱患。在web.xml文件中添加如下代碼:
<session-config> <session-timeout>30</session-timeout> </session-config>
(4)訪問控制:
訪問控制實際上就是創建用戶和權限之間的對應關係,本系統採用的是「基於角色的訪問控制」,根據相應功能模塊的劃分相應的權限,並將相應的權限分配給不一樣的角色,再將角色分配給用戶,用戶就擁有了該角色所持有的權限。具體的設計與實現分析請見1.2 Annotation和Struts2攔截器實現基於角色的垂直權限管理。
如下是本系統的垂直權限管理模塊的物理模型設計:
圖1-2 權限管理模塊物理模型
管理員與角色之間是多對多的關係,這樣能夠實現爲一個管理員分配多個角色進行管理,而角色與權限之間也是多對多的關係,權限是根據功能模塊進行劃分的,使得角色能夠進行細粒度的權限分配。
首先聲明一個註解類,該註解類是自定義的,根據咱們須要的實現的功能進行相應註解的定義,使得在以後須要註釋的方法上使用相應註解,代碼以下:
@Retention(RetentionPolicy.RUNTIME) //註解的存活時間,整個運行時都存活 @Target(ElementType.METHOD) //此註解表示只能在方法上面有效 public @interface Permission { /** 模塊名 **/ String model(); /** 權限值 **/ String privilegeValue(); /*用於區分當前頁面是dialog仍是navTab*/ String targetType(); } 而後定義相應的攔截器類,並在struts2的配置文件struts.xml中進行攔截器的配置,爲相應須要的類進行攔截驗證,定義攔截器部分的核心代碼以下: /** * 校驗控制在方法上的攔截 */ @SuppressWarnings("unchecked") public boolean validate(Admin admin, ActionInvocation invocation, List<Role> roleList, Map session) { String methodName= "execute"; //定義默認的訪問方法 Method currentMethod = null; methodName = invocation.getProxy().getMethod(); //經過actionProxy,獲得當前正在執行的方法名 try { //利用反射,經過方法名稱獲得具體的方法 currentMethod = invocation.getProxy().getAction().getClass().getMethod(methodName, new Class[] {}); } catch (Exception e) { e.printStackTrace(); } if (currentMethod != null && currentMethod.isAnnotationPresent(Permission.class)) { //獲得方法上的Permission註解 Permission permission = currentMethod.getAnnotation(Permission.class); //經過Permission註解構造出系統權限 Systemprivilege privilege = new Systemprivilege(new SystemprivilegeId(permission.privilegeValue(), permission.model())); session.put("targetType", permission.targetType()); //迭代用戶所具備的具體權限,若是包含,返回true for (Role role : roleList) { RolePrivilege rolePrivilege = new RolePrivilege(privilege, role); if (role.getRolePrivileges().contains(rolePrivilege)) { return true; } } return false; } return true; }
本系統須要向權限表中添加相應的功能模塊,爲了有一個比較細的粒度,模塊將根據功能進行劃分,而權限則根據按鈕來進行細分:
圖1-3 數據庫截圖
在定義了相應的權限後,咱們須要在相應的執行方法體上面進行註釋,使得攔截器進行攔截驗證時能根據註釋獲取該執行方法體的模塊和權限類型:
@Permission(model="recharge", privilegeValue="recharge" ,targetType="dialog")
首先添加相應的員工:
而後新增相應的角色,填寫角色名稱並勾取相應的權限:
而後新增相應的用戶,勾選該用戶相應的角色,能夠選擇多個角色,而且選擇相應用戶對應的員工: