防盜鏈技術html
CSRF(模擬請求)前端
分析防止僞造Token請求攻擊java
互聯網API接口冪等性設計mysql
忘記密碼漏洞分析nginx
好比A網站有一張圖片,被B網站直接經過img標籤屬性引入,直接盜用A網站圖片展現。web
若是別人的項目頻繁引用個人圖片的話 別人請求放訪問的是個人 服務器 也會浪費個人寬帶redis
判斷http請求頭Referer域中的記錄來源的值,若是和當前訪問的域名不一致的狀況下,說明該圖片可能被其餘服務器盜用。spring
Referer字段中記錄了訪問的來源(瀏覽器訪問連接地址)sql
http協議中: 請求頭 請求體 請求 數據庫
至關於限制資源(圖片、文字) 只能在某個域名(限制某個服務器)來源進行訪問
在互聯網本質通信底層socket技術,socket技術裏面二進制文件流傳輸,不管是圖片仍是視頻都是玩的二進制文件流進行傳輸
補充若是看不到請求頭:
從哪一個訪問地址過來的信息 如上圖所示
A項目(服務器A):
maven:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <!-- SpringBoot 對lombok 支持 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- SpringBoot web 核心組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- SpringBoot 外部tomcat支持 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- springboot-log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> <!-- springboot-aop 技術 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
Controller:
@Controller public class JspController { private static final Logger logger = LoggerFactory.getLogger(JspController.class); @RequestMapping("/jspIndex") public String jspIndex() { logger.info("springboot 集成logger 日誌成功!!!"); return "jspIndex"; } }
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> <h1>我是A項目....</h1> <img alt="" src="http://toov5.test1.cc:8080/imgs/01.png"> </body> </html>
能夠看到 靜態資源訪問的路徑:
http://toov5.test1.cc:8080/imgs/01.png
B項目(服務器B): 過濾器 + 靜態資源
maven:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <dependencies> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- mysql 依賴 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- SpringBoot 對lombok 支持 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!-- SpringBoot web 核心組件 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </dependency> <!-- SpringBoot 外部tomcat支持 --> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <!-- springboot-log4j --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j</artifactId> <version>1.3.8.RELEASE</version> </dependency> <!-- springboot-aop 技術 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/commons-lang/commons-lang --> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> </dependencies>
Filter:
import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.annotation.WebFilter; import org.apache.catalina.servlet4preview.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Value; // 圖片防盜鏈 @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 { System.out.println("filter"); // 1.獲取請求頭中的來源字段 HttpServletRequest req = (HttpServletRequest) request; String referer = req.getHeader("Referer"); if (StringUtils.isEmpty(referer)) { request.getRequestDispatcher("/imgs/error.png").forward(req, response); return; } // 2.判斷請求頭中的域名是否和限制的域名一致 String domainUrl = getDomain(referer); System.out.println(domainUrl); // 正常狀況 黑名單 白名單接口 if (!domainUrl.equals(domainName)) { request.getRequestDispatcher("/imgs/error.png").forward(req, response); //實際項目中這裏是 從數據庫查詢出來的結果 不是寫死的這樣的 return; } // 直接放行圖片 chain.doFilter(req, response); } /** * 獲取url對應的域名 */ 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() { // TODO Auto-generated method stub } }
須要過濾處理的域名:
spring.mvc.view.prefix=/WEB-INF/jsp/ spring.mvc.view.suffix=.jsp domain.name=toov5.test1.cc:8080
過濾器打印: 此時的訪問url:http://toov5.test1.cc:9090/jspIndex 與配置中的 domain.name=toov5.test1.cc:8080 不符合
B項目實際上就是 過濾器 + 靜態資源 有A服務器想要的資源
實際開發中設置白名單 就是隻能某某某服務器的才能夠哈哈
玩的就是請求頭哈哈 關鍵詞 Referer
(Cross Site Request Forgery, 跨站域請求僞造)是一種網絡的攻擊方式,它在 2007 年曾被列爲互聯網 20 大安全隱患之一,也被稱爲「One Click Attack」或者Session Riding,一般縮寫爲CSRF或者XSRF,是一種對網站的惡意利用也就是人們所知道的釣魚網站。儘管聽起來像跨站腳本(XSS),但它與XSS很是不一樣,而且攻擊方式幾乎相左。XSS利用站點內的信任用戶,而CSRF則經過假裝來自受信任用戶的請求來利用受信任的網站。與XSS攻擊相比,CSRF攻擊每每不大流行(所以對其進行防範的資源也至關稀少)和難以防範,因此被認爲比XSS更具危險性。
會話信息,可能使用Token方式進行保存。
API接口冪等設計 : 保證數據惟一性 不容許有重複的 (防止表單重複提交 )
互聯網API冪等接口設計解決方案:
網絡延遲 重複提交屢次 ~~ 防止
或者惡意的攻擊 重複提交~~ 防止
因此我要防止別人模擬惡意請求啊
多版本併發控制,該策略主要使用 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 來防止,其餘操做對對象的併發更新,致使更新丟失。爲了不失敗,一般須要必定的重試機制。
樂觀鎖 無鎖機制,經過版本字段判斷,若是多線程下。只能有一個操做成功
but若是全部程序都這麼玩兒 會影響效率的!
在插入數據的時候,插入去重表(額外添加一張表),利用數據庫的惟一索引特性,保證惟一的邏輯。
select for update,整個執行過程當中鎖定該訂單對應的記錄。注意:這種在 DB 讀大於寫的狀況下儘可能少用。
業務要求:頁面的數據只能被點擊提交一次
發生緣由:因爲重複點擊或者網絡重發,或者 nginx 重發等狀況會致使數據被重複提交
解決辦法:
集羣環境:採用 token 加 redis(redis 單線程的,處理須要排隊)
單 JVM 環境:採用 token 加 redis 或 token 加 JVM 內存
處理流程:
數據提交前要向服務的申請 token,token 放到 redis 或 jvm 內存,token 有效時間
提交後後臺校驗 token,同時刪除 token,生成新的 token 返回
token 特色: 要申請,一次有效性,能夠限流
客戶端每次在調用接口的時候,須要在請求頭中,傳遞令牌參數,每次令牌只能用一次。
一旦使用以後,就會被刪除,這樣能夠有效防止重複提交。
步驟:
1.生成令牌接口
2. 接口中獲取令牌驗證
令牌方式防止Token重複提交:
補充下:
1.什麼Token(令牌) 表示是一個零時不容許有重複相同的值(臨時且惟一)
2.使用令牌方式防止token重複提交。
使用場景:
在調用第API接口的時候,須要傳遞令牌,該Api接口 獲取到令牌以後,執行當前業務邏輯,讓後把當前的令牌刪除掉。
在調用第API接口的時候,須要傳遞令牌 建議15-2小時
代碼步驟:
1.獲取令牌
2.判斷令牌是否在緩存中有對應的數據
3.若是緩存沒有該令牌的話,直接報錯(請勿重複提交)
4.若是緩存有該令牌的話,直接執行該業務邏輯
5.執行完業務邏輯以後,直接刪除該令牌。
調用接口以前先生成令牌,調用接口時候把令牌存放在請求頭中傳遞過去。而後進行處理
首先獲取token:
不帶token請求:
再次攜帶token:(此時服務器端生成的token被刪除了) 再次攜帶這個token 會執行相應的提示邏輯
執行一次成功以後 會刪除token 下次攜帶tonken會與服務器對應不上而錯誤
tokenUtils:
import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.commons.lang.StringUtils; public class TokenUtils { private static Map<String, Object> tokenMaps = new ConcurrentHashMap<String, Object>(); // 獲取令牌 public static synchronized String getToken() { // 如何在分佈式場景下使用分佈式全局ID實現 String token = "token" + System.currentTimeMillis(); // hashMap好處能夠附帶 關聯值 這裏沒有附帶其餘的 tokenMaps.put(token, token); //好比附帶值 //tokenMaps.put(token, "userId") return token; } public static boolean findToken(String tokenKey) { // 判斷該令牌是否在tokenMap 是否存在 String token = (String) tokenMaps.get(tokenKey); if (StringUtils.isEmpty(token)) { return false; } // token 獲取成功後 刪除掉 tokenMaps.remove(token); return true; } }
實體類:
public class OrderEntity { private int id; private String orderName; private String orderDes; /** * @return the id */ public int getId() { return id; } /** * @param id * the id to set */ public void setId(int id) { this.id = id; } /** * @return the orderName */ public String getOrderName() { return orderName; } /** * @param orderName * the orderName to set */ public void setOrderName(String orderName) { this.orderName = orderName; } /** * @return the orderDes */ public String getOrderDes() { return orderDes; } /** * @param orderDes * the orderDes to set */ public void setOrderDes(String orderDes) { this.orderDes = orderDes; } }
Controller:
import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.itmayeidu.utils.TokenUtils; import com.itmayiedu.entity.OrderEntity; import com.itmayiedu.mapper.OrderMapper; @RestController public class OrderController { @Autowired private OrderMapper orderMapper; @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) { // 代碼步驟: // 1.獲取令牌 存放在請求頭中 String token = request.getHeader("token"); if (StringUtils.isEmpty(token)) { return "參數錯誤!"; } // 2.判斷令牌是否在緩存中有對應的令牌 // 3.如何緩存沒有該令牌的話,直接報錯(請勿重複提交) // 4.如何緩存有該令牌的話,直接執行該業務邏輯 // 5.執行完業務邏輯以後,直接刪除該令牌。 if (!TokenUtils.findToken(token)) { //若是返回false 就提示 請勿操做 return "請勿重複提交!"; } orderEntity.setOrderName("黃燜雞米飯"); orderEntity.setOrderDes("美味"); int result = orderMapper.addOrder(orderEntity); return result > 0 ? "添加成功" : "添加失敗" + ""; } }
下次來查詢時候 因爲執行成功以後會刪除token 因此查詢的結果是false 提示"請勿重複提交"
若是用戶提早屢次生成好token 再惡意重複提交的狀況,如何進行處理?
好比HttpClient去獲取到token而後拿來使用
使用圖形驗證碼防止機器模擬接口請求攻擊,在調用核心業務接口時,好比支付、下單、等接口,最好使用手機短信驗證驗證或者是人臉識別,防止其餘用戶使用token僞造請求。
也能夠經過Nginx實現限流(1分鐘以內接受1000個請求),配置黑名單白名單(若是發現某人惡意請求攻擊,屏蔽他的IP)
市面上沒有百分百的徹底識別驗證碼的工具~
在實際項目中,會話信息使用令牌方式保存。若是黑客利用抓包技術分析到令牌。黑客技術使用令牌僞造支付下單等核心業務。
綁定ip (4g網絡的ip是不固定的)
在互聯網上沒有絕對防止僞造請求。 可是能夠在調用接口時候,確認是本人的操做。使用發送短信驗證碼或者圖像識別的方式。
在覈心接口上,必定要確認是本人操做,好比密碼修改,支付下單等等操做
黑客使用抓包工具分析Http請求,在忘記密碼找回時,須要發送一套短信驗證碼,若是驗證碼數字比較短的話,很容易使用暴力破解方式攻擊破。
防護手段:
忘記密碼驗證碼最好在6-8位。
一旦頻繁調用接口驗證時,應該使用圖形驗證碼攔截,防止機器模擬。
使用黑名單和白名單機制,防護攻擊。
關於:
一、忘記密碼漏洞暴力破解找回密碼
二、使用上傳文件漏洞格式化服務器硬件 注入個rm -rf * 就完蛋了
三、常見其餘攻擊和漏洞(ErrorCode, Html註釋流動,路徑遍歷漏洞)
使用短信驗證碼能夠被破解,在忘記密碼短信找回中有一個code(短信驗證碼)。
提交時候 Java程序 HttpClient技術開啓多線程,暴力破解生成對應的4位數字之內驗證碼進行驗證,若是一旦驗證成功,成功破解。
防止的話就是 找回驗證碼中加入 字母 字母和數字混合使用 若是找回密碼接口重試五次以上仍是錯誤(出現圖形驗證碼)防止繼續模擬
配置防止DDOS,限制IP訪問,配置黑名單白名單。
在作值傳遞時候 慎重隱藏域 <input type="hidden" >
使用上傳文件漏洞格式化服務器硬盤: 對於上傳的文件 要判斷文件流 而不是名字後綴啥的
上傳文件時候 沒有限制格式 致使任意上傳文件 若是黑客上傳木馬文件的(可執行程序)狀況。可能會致使服務崩潰
案例: 上傳木馬文件 刪除某個文件 (jsp exe bat)
解決方案:
方式一: 在上傳文件時候 必定要使用判斷文件流的方式 肯定是圖片 不要判斷後綴方式獲取圖片
方式二: 靜態資源與動態資源分開服務器 Nginx+Tomcat實現 動靜分離 Nginx存放靜態資源 沒有tomcat環境
方式三: 服務器硬盤上不能作刪除操做
方式四: 權限設置 對於目錄的操做權限沒有
方式五: 前端作後綴限制
方式六:服務器上不要有熱部署功能。若是我上傳class文件。 Java程序就能獲取到了。限制 jsp exe 等可執行程序。
上傳文件漏洞原理: jsp裏面有操做文件的代碼 我上傳後 而後訪問這個jsp 在tomcat環境下 執行這個文件 執行了 就完蛋了
用第三方工具類去判斷流
XSS : JS腳本注入 獲取用戶信息(token等等) 防護方式 使用特殊字符轉換的方式 +過濾器攔截處理
CSRF: 跨站模擬請求
保證接口冪等性 : token方式
防止機器識別: token+圖形識別 盡力而爲 沒有百分百破解驗證碼 圖形驗證碼至少能有效阻止 token+圖形識別+限流 有次數限制的
如何防止CSRF 模擬請求 最好使用圖形驗證碼+若是調用核心業務接口 好比支付等安全相關 最好發送短信驗證+人臉識別
若是抓包分析到toeken 能夠僞造下單
直接異常信息,會給攻擊者以提示。 可使用mvc中的工具,把錯誤碼異常等進行封裝
HTML註釋, 會暴露功能,方便攻擊。 上線時去除註釋
文件上傳, 若是自己功能就是上傳文件去執行,那麼就有可能執行很是危險的命令。 解決方式是,設置文件白名單,限制文件類型,另外還能夠從新命名文件,更名爲不可執行的
路徑遍歷, 使用相對路徑來遍歷未開放的目錄。 方式是將JS,CSS部署在獨立的服務器,使用獨立域名。 其餘文件不使用靜態URL訪問,動態參數不包含文件路徑信息。