日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

对付短信发送攻击

發布時間:2023/12/31 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 对付短信发送攻击 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

自從某公司使用短信驗證碼驗證用戶真實性以來,短信便逐漸成了公司業務的標配。現在幾乎每家公司的服務都包含了短信發送這一功能。而用戶請求短信時,一般還未注冊,所以這個接口是匿名接口(不需要登錄)。

于是,壞蛋們就開始搗蛋了。他們通過對軟件抓包,得到用戶的請求消息,然后模擬用戶對服務器瘋狂的發送這一請求,消耗公司的短信資源,騷擾無辜手機用戶,甚至造成短信通道堵塞,無法發出正常消息。搞不好還被那些被騷擾者的投訴,封掉公司的短信通道,或者被各手機廠商識別為垃圾短信從而失去營銷效果,甚至影響正常業務。

今天,我就遇到這么個搗蛋鬼。他的手里有大約上千臺肉雞(科普一下:“肉雞”是指那些可以被黑客操控的無辜者的電腦,這些電腦用戶并不知情。之所以可以被操控,可能是因為安裝了有木馬程序的軟件,或者系統存在漏洞被攻破),于是模擬消息從全國鋪天蓋地而來,服務器日志瘋狂刷屏。于是,我們立刻著手處理這件事情。

解決策略

我們想到了兩種策略:

  • 使用iptables封IP
  • 使用nginx lua + redis
  • 兩種方案各有好處,如果訪問量不大的話,任選其一即可,如果訪問量極高,就需要酌情考慮了。

    我們采取的策略是第二種:

  • 使用嵌入到nginx中的lua程序對用戶請求(僅限短信)+IP進行限定,使其:
    • 在1分鐘內的請求只允許發送1次短信
    • 如果一分鐘內超過1次,但在3次以內,則不發送短信,并給與警告
    • 如果一分鐘內超過3次,則禁止這個IP的任何訪問
  • 使用redis保存這些被封IP以及帶時限的接口請求,以免nginx無法定義全局變量。這樣,nginx就可以實現:
    • 對IP是否允許放行的檢查
    • 令1分鐘前的IP訪問記錄自動過期
  • 剛開始我們也使用了第一種,將這些IP直接加入到iptables中去,從內核層面封掉這些IP,只是我們覺得解封起來比較麻煩,而且無法與我們的軟件集成,尤其是很難實現邏輯集成(區別接口,允許1分鐘1次,3次以上才封IP),遂使用nginx+redis的方案,畢竟lua程序可以寫成我想要的邏輯。

    實現

    假如我們發送短信的接口是 /sendSms
    在nginx.conf文件的 server 段內新建一個location塊,使其匹配正則表達式 ~* sendSms ,然后開始寫代碼吧:

    location ~* sendSms { default_type 'application/json; charset=UTF-8';lua_need_request_body on;access_by_lua ' -- 為了lua代碼可以語法高亮,這里的內容放在后一個代碼塊,請自行復制到此處即可';proxy_pass http://app_backend;proxy_set_header X-Forwarded-For $remote_addr;proxy_pass_header Origin;}

    下面的lua代碼請復制到上文單引號內:

    -- ngx.exit(ngx.OK) local cjson = require "cjson" local ip = ngx.var.remote_addr if ngx.var.http_user_agent ~= nil or ngx.var.http_user_agent == "" thenlocal agent = string.lower(ngx.var.http_user_agent)-- ngx.say("user agent:", ngx.var.http_user_agent)-- ngx.exit(200)local sIdx = string.find(agent, "httpclient") or string.find(agent, "java")-- ngx.say("sIdx:", sIdx)if (sIdx ~= nil) thenngx.status = ngx.HTTP_FORBIDDENlocal msg = "你把硬盤拿過來,我直接把數據庫給你拷貝一份吧,這樣太慢了,我都急死了"ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null}))ngx.exit(ngx.status)returnend end local redis = require "resty.redis" local red = redis.new()red:set_timeout(1000)local ok, err = red:connect("127.0.0.1", 6379) if not ok thenngx.status = ngx.HTTP_INTERNAL_SERVER_ERRORngx.say("failed to connect: ", err)return end local forbidden, err1 = red:sismember("fbdIP", ip) -- ngx.say("forbidden:", forbidden) if forbidden == 1 thenngx.status = ngx.HTTP_FORBIDDENlocal msg = "你把硬盤拿過來,我直接把數據庫給你拷貝一份吧,這樣太慢了,我都急死了"ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null}))ngx.exit(ngx.status)return end local key = "ip::" .. ip -- ngx.say("key:", key) local ttl, err1 = red:ttl(key) if ttl == -1 thenred:del(key) endlocal res, err = red:get(key) red:incr(key) if (not res) or (res == ngx.null) then--[[local msg = "failed to get cache"ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null}))--]]red:expire(key, 55) -- 55秒內不允許同一IP超過30次訪問ngx.exit(ngx.OK) elseif tonumber(res) < 1 thenngx.exit(ngx.OK) elseif tonumber(res) >= 1 thenngx.status = 200 -- ngx.say("redis result is string 1")-- local msg = "我們認為你有惡意請求的嫌疑,請不要使用及其程序進行訪問"local msg = "慢點,無影手得多累啊"ngx.say(cjson.encode({code=16, msg=msg, R=cjson.null}))if tonumber(res) > 10 thenred:sadd("fbdIP", ip)endngx.exit(ngx.status)return end

    解釋

    • 前面對key的檢查不止使用了get() ,還使用了 ttl(),是因為redis的過期回收策略使用的是一種近似LRU算法,導致一定概率的不刪除,所以使用ttl進行檢查。

    前提

    在大家讀到這篇文章的時候,我要順便說一下這個方案適用的前提。如果沒有這些前提,那這個方案對你就不可用,當然,解決思路也許可以有些幫助,如果你善于動手的話,很快也能弄好自己的解決方法。我們的系統滿足以下幾個條件:

  • 當然是使用nginx做前端代理的web架構了
  • 使用支持lua的nginx。直接從nginx官網下載按照的nginx是不支持的,需要額外下載lua代碼,并編譯到nginx中去。或者直接使用openresty
  • 建議

    由于之前redis有個可以拿到root權限的漏洞,所以:

  • 務必要對redis設置訪問密碼
  • 最好將redis服務的綁定IP限定在內網IP上
  • redis 的key千萬不能被污染,否則正常用戶的IP會被誤傷封禁。

    工具

    在解決這個問題的時候,我們也是用廢了很多腦細胞的,為了節省你的腦細胞,我就免費讓你看看。

    怎么得知壞蛋在攻擊我的服務器呢?

    我的辦法是:
    通過將日志內所有對/sendSms接口的調用IP進行統計,找到那些調用次數比較多的,比如大于10次的。用這個命令就好了:

    grep "POST /sendSms" logs/ikuaiyue.log | awk '{print $6}' | sed s/IP:// | sort | uniq -c | awk '{print $1 "\t" $2}' | sort -n

    然后就會看到這樣的結果:

    1 115.205.13.179 2 117.136.40.20 2 117.136.94.44 2 117.59.39.22 122 223.104.10.28

    第一列是此IP的調用次數,第二列你懂。
    好了,現在知道改怎么辦了吧?

    順便說一下,我們的日志是這個樣子滴:

    [2016-05-30 01:25:20.451] [INFO] normal - IP:117.174.26.32 POST /sendSms [2016-05-30 01:26:17.918] [INFO] normal - IP:117.174.26.32 POST /sendSms ...

    稍微解釋下上面的命令:

    grep "POST /sendSms" logs/ikuaiyue.log | awk '{print $6}' #按空格分割后的第6列(即IP:117.174.26.32)| sed s/IP:// #刪除字符"IP:"| sort #排序| uniq -c #去重,并記下重復數,相當與做了個統計操作#此時重復數字為第一列,IP被放在了第二列.#但此時格式上有個問題:首列數字是右對齊的| awk '{print $1 "\t" $2}' #為了解除其右對齊,重新打印以便,并以tab分隔| sort -n #以首列為依據排序。-n表示當做數字來排列,默認是當做字符串的

    當你按我的方法設置好了nginx后,怎么知道有沒有起作用呢?

    把剛剛那個命令改改,只輸出最后3000行(具體數字看你的業務繁忙程度了)用做統計:

    tail -n3000 logs/ikuaiyue.log | grep "POST /sendSms" | awk '{print $6}' | sed s/IP:// | sort | uniq -c | awk '{print $1 "\t" $2}' | sort -n

    總結

    以上是生活随笔為你收集整理的对付短信发送攻击的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。