1. 程式人生 > >如何識別惡意請求,進行反爬蟲操作?

如何識別惡意請求,進行反爬蟲操作?

前言

最近這幾天,真的越來越感受到了。業務需求推動技術的發展。沒有業務需求支援,一切都是扯。

之前在知乎回答了一個問題突然火了,導致我的小程式流量暴增,如下圖:

 

                           

  

                             

 

最高峰的時候,每分鐘200多個不同ip請求。大概每秒5個請求。也就是5QPS。(突然感覺好小好小)

 

我這個系統有限流,有快取,QPS上千是沒什麼問題的。

所以今天我想寫的不是高併發,而是如何識別惡意請求,惡意攻擊,並且攔截他們。

因為程式碼是開源的,介面什麼的完全暴漏出去了,所以總會有些人,惡意請求我的介面,雖然沒啥大的影響,但總歸很不爽。

 

限制ip

這個也是我一直都有的程式碼,具體如下:

  1 package com.gdufe.osc.interceptor;
  2 
  3 import com.alibaba.fastjson.JSON;
  4 import com.gdufe.osc.common.OscResult;
  5 import com.gdufe.osc.enums.OscResultEnum;
  6 import com.gdufe.osc.service.RedisHelper;
  7 import com.gdufe.osc.utils.IPUtils;
  8 import lombok.extern.slf4j.Slf4j;
  9 import org.apache.commons.lang3.StringUtils;
 10 import org.springframework.beans.factory.annotation.Autowired;
 11 import org.springframework.lang.Nullable;
 12 import org.springframework.web.servlet.HandlerInterceptor;
 13 import org.springframework.web.servlet.ModelAndView;
 14 
 15 import javax.servlet.http.HttpServletRequest;
 16 import javax.servlet.http.HttpServletResponse;
 17 import java.util.Map;
 18 
 19 /**
 20  * @Author: yizhen
 21  * @Date: 2018/12/28 12:11
 22  */
 23 @Slf4j
 24 public class IPBlockInterceptor implements HandlerInterceptor {
 25 
 26     /** 10s內訪問50次,認為是刷介面,就要進行一個限制 */
 27     private static final long TIME = 10;
 28     private static final long CNT = 50;
 29     private Object lock = new Object();
 30 
 31     /** 根據瀏覽器頭進行限制 */
 32     private static final String USERAGENT = "User-Agent";
 33     private static final String CRAWLER = "crawler";
 34 
 35     @Autowired
 36     private RedisHelper<Integer> redisHelper;
 37 
 38     @Override
 39     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 40         synchronized (lock) {
 41             boolean checkAgent = checkAgent(request);
 42             boolean checkIP = checkIP(request, response);
 43             return checkAgent && checkIP;
 44         }
 45     }
 46 
 47     private boolean checkAgent(HttpServletRequest request) {
 48         String header = request.getHeader(USERAGENT);
 49         if (StringUtils.isEmpty(header)) {
 50             return false;
 51         }
 52         if (header.contains(CRAWLER)) {
 53             log.error("請求頭有問題,攔截 ==> User-Agent = {}", header);
 54             return false;
 55         }
 56         return true;
 57     }
 58 
 59     private boolean checkIP(HttpServletRequest request, HttpServletResponse response) throws Exception {
 60         String ip = IPUtils.getClientIp(request);
 61         String url = request.getRequestURL().toString();
 62         String param = getAllParam(request);
 63         boolean isExist = redisHelper.isExist(ip);
 64         if (isExist) {
 65             // 如果存在,直接cnt++
 66             int cnt = redisHelper.incr(ip);
 67             if (cnt > IPBlockInterceptor.CNT) {
 68                 OscResult<String> result = new OscResult<>();
 69                 response.setCharacterEncoding("UTF-8");
 70                 response.setHeader("content-type", "application/json;charset=UTF-8");
 71                 result = result.fail(OscResultEnum.LIMIT_EXCEPTION);
 72                 response.getWriter().print(JSON.toJSONString(result));
 73                 log.error("ip = {}, 請求過快,被限制", ip);
 74                 // 設定ip不過期 加入黑名單
 75                 redisHelper.set(ip, --cnt);
 76                 return false;
 77             }
 78             log.info("ip = {}, {}s之內第{}次請求{},引數為{},通過", ip, TIME, cnt, url, param);
 79         } else {
 80             // 第一次訪問
 81             redisHelper.setEx(ip, IPBlockInterceptor.TIME, 1);
 82             log.info("ip = {}, {}s之內第1次請求{},引數為{},通過", ip, TIME, url, param);
 83         }
 84         return true;
 85     }
 86 
 87     private String getAllParam(HttpServletRequest request) {
 88         Map<String, String[]> map = request.getParameterMap();
 89         StringBuilder sb = new StringBuilder("[");
 90         map.forEach((x, y) -> {
 91             String s = StringUtils.join(y, ",");
 92             sb.append(x + " = " + s + ";");
 93         });
 94         sb.append("]");
 95         return sb.toString();
 96     }
 97 
 98     @Override
 99     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
100     }
101 
102     @Override
103     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
104     }
105 }

 

程式碼我大致解釋一個。

可以看到41行和42行程式碼;我做了兩層的攔截:

第一層是先攔截不合規的瀏覽器頭,比如瀏覽器頭包含有爬蟲的資訊,全部攔截掉。

第二層是一個ip的攔截。如果在10s之內,訪問我的介面大於50次,我就認為你是刷介面過快,是一個爬蟲。

此時我直接存入redis,永不過期,下次直接攔截掉。

這是第一個辦法。

 

統計ip訪問次數

但總有些ip訪問很慢,比如10s才訪問,20-30次,但又不間斷的訪問,爬取,永不停歇。

雖然沒啥大的影響,總歸很不爽。

我們看看程式大致列印的日誌把:

2019-06-01 16:21:24.271 [http-nio-8083-exec-5] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 106.121.145.154, 10s之內第1次請求zhihu/spider/get,引數為[type = 1;offset = 80;limit = 10;],通過
2019-06-01 16:21:24.271 [http-nio-8083-exec-5] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:356
2019-06-01 16:21:24.775 [http-nio-8083-exec-3] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 120.229.218.95, 10s之內第1次請求zhihu/spider/get,引數為[type = 1;offset = 70;limit = 10;],通過
2019-06-01 16:21:24.775 [http-nio-8083-exec-3] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:612
2019-06-01 16:21:32.050 [http-nio-8083-exec-10] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 105.235.134.202, 10s之內第1次請求zhihu/spider/get,引數為[type = 2;offset = 0;limit = 10;],通過
2019-06-01 16:21:32.050 [http-nio-8083-exec-10] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:93
2019-06-01 16:21:32.320 [http-nio-8083-exec-7] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 120.229.218.95, 10s之內第2次請求zhihu/spider/get,引數為[type = 1;offset = 80;limit = 10;],通過
2019-06-01 16:21:32.320 [http-nio-8083-exec-7] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:100
2019-06-01 16:21:33.755 [http-nio-8083-exec-2] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 106.17.6.118, 10s之內第1次請求zhihu/spider/get,引數為[type = 1;offset = 80;limit = 10;],通過
2019-06-01 16:21:33.755 [http-nio-8083-exec-2] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:107
2019-06-01 16:21:33.805 [http-nio-8083-exec-9] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 123.120.29.78, 10s之內第1次請求zhihu/spider/get,引數為[type = 1;offset = 80;limit = 10;],通過
2019-06-01 16:21:33.805 [http-nio-8083-exec-9] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:1057
2019-06-01 16:21:35.697 [http-nio-8083-exec-6] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 106.121.145.154, 10s之內第1次請求zhihu/spider/get,引數為[type = 1;offset = 90;limit = 10;],通過
2019-06-01 16:21:35.697 [http-nio-8083-exec-6] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:1030
2019-06-01 16:21:36.197 [http-nio-8083-exec-1] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 120.229.218.95, 10s之內第1次請求zhihu/spider/get,引數為[type = 2;offset = 0;limit = 10;],通過
2019-06-01 16:21:36.198 [http-nio-8083-exec-1] INFO  c.gdufe.osc.service.impl.ZhiHuSpiderImpl - [] - 圖片隨機位置為:2384
2019-06-01 16:21:36.725 [http-nio-8083-exec-8] INFO  c.g.osc.interceptor.IPBlockInterceptor - [] - ip = 183.236.187.208, 10s之內第1次請求zhihu/spider/get,引數為[type = 1;offset = 0;limit = 10;],通過

 

一個訪問ip,應該會打印出兩條日誌。一條日誌他的ip以及訪問的路徑。一條則與本題無關。

但我如何統計每個ip總共訪問了多少次呢?

 

Shell程式碼如下:

 

 1 #!/bin/bash 
 2 # 複製日誌到當前目錄
 3 cp /home/tomcat/apache-tomcat-8.5.23/workspace/osc/osc.log /home/shell/java/osc.log 
 4 # 將日誌中的ip點號如: 120.74.147.123 換為 120:74:147:123
 5 sed -i "s/\./:/g" osc.log
 6 # 篩選出只包含ip的行,並且只打印ip出來
 7 awk '/limit/ {print $11}' osc.log > temp.txt
 8 # 根據ip的所有位數進行排序 並且統計次數 最後輸出前50行
 9 cat temp.txt | sort -t ':' -k1n -k2n -k3n -k4n | uniq -c | sort -nr | head -n 50 > result.txt
10 # 刪除無關緊要檔案
11 rm -rf temp.txt osc.log 

 

這其中涉及到了好多命令,都是今天臨時一一學的(臨時抱佛腳)。

最後執行結果如下:    

第一列是訪問的次數,第二列是ip。

那麼一看43.243.12.43這個ip就不正常了。肯定是爬蟲來的。那麼直接封了就是。

 

後言

 

業務需求推動技術真的學到了。有需求,有業務,才會推動技術的進步。

 

各位大佬們,還有沒有其他反爬蟲的技巧。一起交流一下。