互聯網安全架構平臺設計

常見的Web安全漏洞

(1). XSS 攻擊

什麼是XSS攻擊手段  

XSS攻擊使用Javascript腳本注入進行攻擊

例如在提交表單後,展示到另一個頁面,可能會受到XSS腳本注入,讀取本地cookie遠程發送給黑客服務器端。

<script>alert('sss')</script>
< < &ltscript>
> >
<script>window.location.href='http://www.baidu.com';</script>
對應html源代碼: <script>alert('sss')</script>
最好使用火狐瀏覽器演示效果
如何防禦XSS攻擊
將腳本特殊字符,轉換成html源代碼進行展示。
漢子編碼http://www.mytju.com/classcode/tools/encode_gb2312.asp
步驟:編寫過濾器攔截所有getParameter參數,重寫httpservletwrapp方法
將參數特殊字符轉換成html源代碼保存.
// 重寫HttpServletRequestWrapper 防止XSS攻擊 public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { private HttpServletRequest request; /** * @param request */ public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); this.request = request; } @Override public String getParameter(String name) { // 過濾getParameter參數 檢查是否有特殊字符 String value = super.getParameter(name); System.out.println("value:" + value); if (!StringUtils.isEmpty(value)) { // 將中文轉換爲字符編碼格式,將特殊字符變爲html源代碼保存 value = StringEscapeUtils.escapeHtml(value); System.out.println("newValue:" + value); } return value; } }
SpringBoot啓動加上@ServletComponentScan
@SpringBootApplication @ServletComponentScan public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }

 (2).SQL注入攻擊

name liu ' or 1=1
select * from table where name=liu' or 1=1
liu ' or 1=1
什麼是SQL注入
SQL注入:利用現有應用程序,將(惡意)的SQL命令注入到後臺數據庫執行一些惡意的
作。
造成SQL注入的原因是因爲程序沒有有效過濾用戶的輸入,使攻擊者成功的向服務器提交惡意的SQL查詢代碼,程序在接收後錯誤的將攻擊者的輸入作爲查詢語句的一部分執行,導致原始的查詢邏輯被改變,額外的執行了攻擊者精心構造的惡意代碼
SQL注入防攻擊手段
不要使用拼接SQL語句方式、最好使用預編譯方式,在mybatis編寫sql語句的時候,最好使用?傳參數方式,不要使用#傳參數,因爲#傳參數方式,可能會受到sql語句攻擊。
演示案例:
http://127.0.0.1:8080/login?userName='liusi'&password='123'
http://127.0.0.1:8080/login?userName='liusi'&password='123' or 1=1
@RestController public class LoginController { @Autowired private UserMapper userMapper; @RequestMapping("/login") public String login(UserEntity userEntity) { System.out.println("賬號密碼信息:userEntity:" + userEntity.toString()); UserEntity login = userMapper.login(userEntity); return login == null ? "登陸失敗!" : "登陸成功!"; } } public interface UserMapper { @Select(" SELECT * FROM user_info where userName=${userName} and password=${password}") public UserEntity login(UserEntity userEntity); }
MyBatis #與?區別
#{}: 解析爲一個 JDBC 預編譯語句(prepared statement)的參數標記符,一個 #{ } 被解析爲一個參數佔位符,可以防止SQL注入問題。
${}: 僅僅爲一個純碎的 string 替換,在動態 SQL 解析階段將會進行變量替換。
 
 
Http請求防盜鏈
(1).什麼是防盜鏈
比如A網站有一張圖片,被B網站直接通過img標籤屬性引入,直接盜用A網站圖片展示。
 
如何實現防盜鏈
判斷http請求頭Referer域中的記錄來源的值,如果和當前訪問的域名不一致的情況下,說明該圖片可能被其他服務器盜用。
使用過濾器判斷請求頭Referer記錄請求來源
@WebFilter(filterName = "imgFilter", urlPatterns = "/imgs/*") public class ImgFilter implements Filter { @Value("${domain.name}") private String domainName; public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; String referer = req.getHeader("Referer"); if (StringUtils.isEmpty(referer)) { request.getRequestDispatcher("/imgs/error.png").forward(request, response); return; } String domain = getDomain(referer); if (!domain.equals(domainName)) { request.getRequestDispatcher("/imgs/error.png").forward(request, response); return; } chain.doFilter(request, response); } /** * 獲取url對應的域名 * * @param url * @return */ public String getDomain(String url) { String result = ""; int j = 0, startIndex = 0, endIndex = 0; for (int i = 0; i < url.length(); i++) { if (url.charAt(i) == '/') { j++; if (j == 2) startIndex = i; else if (j == 3) endIndex = i; } } result = url.substring(startIndex + 1, endIndex); return result; } public void destroy() { } }
 
注意測試的時候,最好開啓兩個不同的瀏覽器測試,避免圖片緩存的原因
(2).CSRF攻擊
CSRF攻擊產生的原因
(Cross Site Request Forgery, 跨站域請求僞造)是一種網絡的攻擊方式,它在 2007 年曾被列爲互聯網 20 大安全隱患之一,也被稱爲“One Click Attack”或者Session Riding,通常縮寫爲CSRF或者XSRF,是一種對網站的惡意利用也就是人們所知道的釣魚網站。儘管聽起來像跨站腳本(XSS),但它與XSS非常不同,並且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則通過僞裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊往往不大流行(因此對其進行防範的資源也相當稀少)和難以防範,所以被認爲比XSS更具危險性。
防禦CSRF攻擊手段
使用圖形驗證碼防止機器模擬接口請求攻擊,在調用核心業務接口時,比如支付、下單、等接口,最好使用手機短信驗證驗證或者是人臉識別,防止其他用戶使用Token僞造請求。
 
API接口冪等性設計方案 
(1).MVCC方案
 多版本併發控制,該策略主要使用 update with condition(更新帶條件來防止)來保證多次外部請求調用對系統的影響是一致的。在系統設計的過程中,合理的使用樂觀鎖,通過 version 或者 updateTime(timestamp)等其他條件,來做樂觀鎖的判斷條件,這樣保證更新操作即使在併發的情況下,也不會有太大的問題。例如
select * from tablename where condition=#condition# // 取出要跟新的對象,帶有版本 versoin
update tableName set name=#name#,version=version+1 where version=#version#
在更新的過程中利用 version 來防止,其他操作對對象的併發更新,導致更新丟失。爲了避免失敗,通常需要一定的重試機制。
去重表
在插入數據的時候,插入去重表,利用數據庫的唯一索引特性,保證唯一的邏輯。
悲觀鎖
select for update,整個執行過程中鎖定該訂單對應的記錄。注意:這種在 DB 讀大於寫的情況下儘量少用。
Token機制,防止頁面重複提交
 業務要求:頁面的數據只能被點擊提交一次
 發生原因:由於重複點擊或者網絡重發,或者 nginx 重發等情況會導致數據被重複提交
解決辦法:
集羣環境:採用 token 加 redis(redis 單線程的,處理需要排隊)
單 JVM 環境:採用 token 加 redis 或 token 加 jvm 內存
處理流程:
數據提交前要向服務的申請 token,token 放到 redis 或 jvm 內存,token 有效時間
提交後後臺校驗 token,同時刪除 token,生成新的 token 返回
token 特點:要申請,一次有效性,可以限流
基於Token方式防止API接口冪等
客戶端每次在調用接口的時候,需要在請求頭中,傳遞令牌參數,每次令牌只能用一次。
一旦使用之後,就會被刪除,這樣可以有效防止重複提交。
步驟:
1. 生成令牌接口
2. 接口中獲取令牌驗證
 
生成令牌接口
public class TokenUtils { private static Map<String, Object> tokenMap = new ConcurrentHashMap<String, Object>(); // 獲取token public static synchronized String getToken() { // 1.生成令牌 String token = "token-" + System.currentTimeMillis(); // 2.存入tokenMap tokenMap.put(token, token); return token; } // 驗證token,並且刪除對應的token public static Boolean exisToken(String token) { // 1.從集合中獲取token Object result = tokenMap.get(token); if (result == null) { return false; } // 2.刪除對應的token tokenMap.remove(token); return true; } }
接口中獲取令牌驗證
 
@RestController public class OrderController { @Autowired private OrderMapper orderMapper; // 獲取Token @RequestMapping("/getToken") public String getToken() { return TokenUtils.getToken(); } // 驗證Token @RequestMapping(value = "/addOrder", produces = "application/json; charset=utf-8") public String addOrder(@RequestBody OrderEntity orderEntity, HttpServletRequest request) { String token = request.getHeader("token"); if (StringUtils.isEmpty(token)) { return "參數錯誤!"; } if (!TokenUtils.exisToken(token)) { return "請勿重複提交!"; } int result = orderMapper.addOrder(orderEntity); return result > 0 ? "添加成功" : "添加失敗" + ""; } }
(2).忘記密碼漏洞
黑客使用抓包工具分析Http請求,在忘記密碼找回時,需要發送一套短信驗證碼,如果驗證碼數字比較短的話,很容易使用暴力破解方式攻擊破。
防禦手段:
忘記密碼驗證碼最好在6-8位。
一旦頻繁調用接口驗證時,應該使用圖形驗證碼攔截,防止機器模擬。
使用黑名單和白名單機制,防禦攻擊。
(3).上傳文件漏洞
漏洞描述
上傳漏洞這個顧名思義,就是攻擊者通過上傳木馬文件,直接得到WEBSHELL,危害等級超級高,現在的入侵中上傳漏洞也是常見的漏洞。
導致該漏洞的原因在於代碼作者沒有對訪客提交的數據進行檢驗或者過濾不嚴,可以直接提交修改過的數據繞過擴展名的檢驗。
漏洞危害
1)可以得到WEBSHELL
2)上傳木馬文件,可以導致系統癱瘓
Tomcat虛擬地址:
F:\itmayiedujiangke2018-06-12\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\wtpwebapps\tomcat_web
環境搭建
Maven依賴
<dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.4</version> </dependency> </dependencies>
 
Index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <form action="/load/UploadServlet" method="post" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" value="submit" /> </form> </body> </html>
 
UploadServlet
public class UploadServletextends HttpServlet { /** * 文件上傳 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) { String root = request.getServletContext().getRealPath("/upload"); DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); try { List<FileItem> list = upload.parseRequest(request); for (FileItem it : list) { // 如果是file文件類型 if (!it.isFormField()) { it.write(new File(root + "/" + it.getName())); response.getWriter().write("success"); } } } catch (Exception e) { try { response.getWriter().write("exception"); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } }
 
腳本文件
創建一個a.jsp
<%@page import="java.io.File"%> <%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <% String root ="F:\\itmayiedu"; File file = new File(root); file.delete(); %>
修復方案
前臺頁面、後臺、服務器都要檢查上傳文件的格式是否正確
1)對文件格式限制,只允許某些格式上傳
2)對文件格式進行校驗,前端跟服務器都要進行校驗(前端校驗擴展名,服務器校驗擴展名、Content_Type等)
3)將上傳目錄防止到項目工程目錄之外,當做靜態資源文件路徑,並且對文件的權限進行設定,禁止文件下的執行權限。
 
判斷文件流是否爲圖片格式
/** * 文件上傳 */ protected void doPost(HttpServletRequest request, HttpServletResponse response) { String root = request.getServletContext().getRealPath("/upload"); DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload upload = new ServletFileUpload(factory); try { List<FileItem> list = upload.parseRequest(request); for (FileItem it : list) { // 如果是file文件類型 if (!it.isFormField()) { FileType fileType = getFileType(it.getInputStream()); if (fileType == null) { // 非圖片格式 response.getWriter().write("fail"); return; } String imgValue = fileType.getValue(); System.out.println("imgValue:" + imgValue); // 是圖片格式 it.write(new File(root + "/" + it.getName())); response.getWriter().write("success"); } } } catch (Exception e) { try { response.getWriter().write("exception"); } catch (IOException e1) { e1.printStackTrace(); } e.printStackTrace(); } } // 判斷文件是圖片格式 public static FileType getFileType(InputStream is) throws IOException { byte[] src = new byte[28]; is.read(src, 0, 28); 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; String hv = Integer.toHexString(v).toUpperCase(); if (hv.length() < 2) { stringBuilder.append(0); } stringBuilder.append(hv); } FileType[] fileTypes = FileType.values(); for (FileType fileType : fileTypes) { if (stringBuilder.toString().startsWith(fileType.getValue())) { return fileType; } } return null; }
 
其他攻擊和漏洞
直接異常信息,會給攻擊者以提示。 可以使用mvc中的工具,把錯誤碼異常等進行封裝
HTML註釋, 會暴露功能,方便攻擊。 上線時去除註釋
文件上傳, 如果本身功能就是上傳文件去執行,那麼就有可能執行非常危險的命令。 解決方式是,設置文件白名單,限制文件類型,另外還可以重新命名文件,改名爲不可執行的
路徑遍歷, 使用相對路徑來遍歷未開放的目錄。 方式是將JS,CSS部署在獨立的服務器,使用獨立域名。 其他文件不使用靜態URL訪問,動態參數不包含文件路徑信息。
網站安全漏洞掃描
 
信息加密與密鑰管理
API安全接口安全設計
 
電子商務風控系統
黑名單與白名單系統
 
基於多種方式實現防禦DDOS攻擊
1.URL特殊字符處理
如rpc遠程調用的時候需要加密,加密產生的+ ?等特殊字符 與http協議中的特殊字符相同,會導致產生空格; http://www.baidu.com?a=urlencode(a)
使用java提供的urlencoder;
注意 不要對整個url進行編碼,只對參數進行編碼
 
2.加密
(1).對稱加密:約定好生成一致的公鑰和私鑰
      DES、 AES、 3DES
A加密
A解密
對稱加密缺點:祕鑰是同一個,客戶端保存的祕鑰容易被暴露,如:app被反編譯等。
對稱加密優點:加解密速度快,適用於 後臺與後臺之間通訊的加密,不容易被截取的情況。
 

 

(2).非對稱加密:配對密鑰,一對
   RSA
公鑰
私鑰
(3).移動APP接口安全設計
Https傳輸,使用令牌,使用非對稱加密

(4).基於令牌方式實現接口參數安全傳輸

服務器端將參數通過接口調用的方式先傳遞給第三方系統,生成令牌(如訂單號),頁面再帶上訂單號進行跳轉,第三方拿到令牌後,從數據庫中再去取數據。
(5).接口簽名
原理:客戶端對傳送的參數字典排序後,進行md5簽名。服務器接收參數後,同理進行參數字典排序後md5簽名。若客戶端與服務端的簽名一致,說明參數未被篡改,校驗通過。

請求參數示例:

字段 數據 類型 描述
phone 158112341234             String             手機號
timestamp 1538990392850    long                 時間戳
deviceId mo-231odvepqd       String              設備id
sign 158112341234               String               接口簽名,

md5(參數字典排序後加鹽)
注:
1.參數排序時,不包括sign參數
2.加鹽參數使用desKey,放在參數字典排序後的開頭位置
3.遇到數值拼接時,需統一轉換成字符串類型。如:123 需轉換成 “123”
4.爲避免客戶端和服務器端簽名對null處理不一致,若參數值爲null時,請轉換成空字符串("")

簽名步驟
1.字段字典排序:
deviceId phone timestamp
2.參數拼接:
mo-231odvepqd1581123412341538990392850

3.加鹽:
hechaojie.commo-231odvepqd1581123412341538990392850

這裏加鹽參數爲:hechaojie.com,請注意安全存儲。

4.md5簽名:
21bf08491dbf4013a8e9275f1d603e4a

客戶端發送數據示例:
{"phone":"158112341234","sign":"21bf08491dbf4013a8e9275f1d603e4a","deviceId":"mo-231odvepqd","timestamp":1538990392850}

 

 

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