當我遇到了爬蟲

腐汝發表於2024-06-03

運維同學,線上機器怎麼又雙叒叕掛了?

對爬蟲也是相愛相殺多年,我對線上爬蟲的應對出現的幾個階段:

1、分析日誌,找出異常請求,封ip。

2、透過waf,針對某個uri ,進行限流(並且人機識別),控制的還是源ip,起初有點成效。

3、遇到大量單ip,觸發不到人機識別,透過nginx自帶的limit_req_zone進行uri限制,發現有效果,但是會誤殺很多無辜。

map $request_uri $xx_limit {
    "~*/xxx/" "/xxx/";
    default "";
}
limit_req_zone $xx_limit zone=api_limit:10m rate=5r/s;

當請求路徑包含/xxx/時,設定$xx_limit 為/xxx/,預設值為空,為空則limit_req_zone 不生效




limit_req zone=api_limit burst=10 delay=5;

每秒標準5個請求,burst可以讓突發請求到10個,delay是當有突發請求時前五個直接請求,剩餘的rate=5/s=200ms/個,消費1個,等待200ms

4、透過openresty,將請求相關資料傳送到請求資料分析介面,分析之後返回結果,再由openresty判斷是否放行,到目前為止立竿見影。

資料分析介面大概得思路就是:此時做一些限制已經沒有用了(後端程式碼也無法更改),去推理人的行為,一個正常人要訪問網站時會怎麼操作,爬蟲畢竟只是爬蟲,沒法跟人一樣。

local http = require "resty.http"
local httpc = http.new()
local json = require("cjson")


local headers=ngx.req.get_headers()
local ip=headers["X-REAL-IP"] or headers["X_FORWARDED_FOR"] or ngx.var.remote_addr
local ua=headers["User-Agent"] or ''
local white_ua={"xxxx",} -- ua 白名單


local api_server="xxx:1234"
local topic_id = "xxxxx"

function req_fast_api()
    --return false 是異常請求,true是正常請求

    local data = {
        TopicId = "xxxxxxx",
        From = 1710086694000,
        To = 1717151565000,
        Query = '"remote_addr:\"' .. ip .. '\""',
        Limit = 100
    }

    local jsonStr = json.encode(data)

    local resp, err = httpc:request_uri("http://" .. api_server .. "/xxx/xxx/xxxx", {
        method = "POST",
        body = jsonStr,
        headers = {
            ["Content-Type"] = "application/json",
        },
    })

    if not resp then
        ngx.log(ngx.ERR, "cls 日誌介面掛了。")
        return 500, "true"
    end

    return resp.status, resp.body
end


function main()
    -- 校驗ua是否在白名單中
    local outer_i
    for i, v in ipairs(white_ua) do
        if string.find(ua, v) then
            break
        else
            outer_i = i
        end
    end

    if outer_i == #white_ua then
        -- ua不在白名單中
        ngx.header["Server"] = "xxxxx/xxx"
        local status, body = req_fast_api()
        ngx.log(ngx.ERR, ip .. "日誌介面返回:" .. body .. ",且ua不在白名單中。")
        if body == "false" then
            ngx.status = ngx.HTTP_BAD_REQUEST
            ngx.say("xxxx")
            ngx.exit(ngx.HTTP_BAD_REQUEST)
        end
    end
end

main()

如果這個方式不行,後面會繼續更新,大概方法分享給深受爬蟲摧殘的運維小夥伴們。

相關文章