最近一個項目發現手機驗證碼總是被人盜刷,一秒鐘刷了1百多個,很明顯這種行為是通過軟體自動送出的,自動發帖機原理類似,解決這個問題目前有兩個方案。
出現這個問題原因:請求手機驗證碼Api時沒有任何帶任何驗證,隻要請求了手機号正确就執行發送操作,軟體或代碼很容易僞造請求過程。
解決方案有很多種,可以選擇下面一種或幾種組合起來使用。
方案1:使用者擷取手機驗證碼時候彈出圖檔驗證碼,輸入後再發送。
優點:增加僞造請求成功的難度,必須輸入驗證碼才可以發送,如果是軟體,軟體需要有圖檔驗證碼識别功能。
缺點:使用者體驗不好,增加了普通使用者的操作步驟。
方案2:增加ip黑名單,即檢測請求ip的發送頻率,如同一個ip一分鐘内請求多少次後屏蔽ip。
缺點:一些軟體有主動更換ip的功能,這種方式效果不是很好。
方案3:加上token密碼驗證
在Api中增加密碼驗證,即每次請求必須帶上token,token驗證正确了才執行發送操作。
這個方案為了替代方案1,因為有的公司對前端體驗要求極高,增加使用者操作步驟後使用者體驗不好。
這裡token可以放在資料庫中,也可以放在記憶體中,個人建議放在記憶體中,速度快,查詢快,至于備援的問題,可以增加一個BuildDate來儲存生成時間。
Token描述類
public class TokenDescriptor
{
///
/// 用戶端唯一Id,必須保證唯一性
public string ClientId { get; set; }
/// token
public string Token { get; set; }
/// token生成日期
public DateTime BuildDate { get; set; }
}
token處理類
public class TokenFactoryBLL
private string _ClientId;
private static List _TokenList;
static TokenFactoryBLL()
_TokenList=new List();
public TokenFactoryBLL(HttpRequestBase httpRequestBase)
_ClientId=StringHelper.ClientId(httpRequestBase);
ClearExpired();
/// 可用于遠端api
public TokenFactoryBLL(string clientId)
_ClientId=clientId;
/// 生成密碼
public string Get()
if (string.IsNullOrEmpty(_ClientId))
return null;
string token=Guid.NewGuid().ToString("N");//guid;
TokenDescriptor tokenDescriptor=new TokenDescriptor();
tokenDescriptor.ClientId=_ClientId;
tokenDescriptor.Token=StringHelper.NewGuid();
tokenDescriptor.BuildDate=Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
_TokenList.Add(tokenDescriptor);
return token;
/// 驗證token
public TipsInfo Validate(string token)
TipsInfo tipsInfo=new TipsInfo();
if (!_TokenList.Exists(c=> c.ClientId.Equals(_ClientId, StringComparison.OrdinalIgnoreCase) && c.Token.Equals(token, StringComparison.OrdinalIgnoreCase)))
tipsInfo.State=0;
tipsInfo.Msg="token密碼驗證失敗";
return tipsInfo;
/// 移除對應用戶端的所有token
public void Remove()
_TokenList.RemoveAll(c=> c.ClientId.Equals(_ClientId, StringComparison.OrdinalIgnoreCase));
/// 清理過期的token,過期時間10分鐘
private static void ClearExpired()
for (var i=0; i < _TokenList.Count; i++)
TokenDescriptor item=_TokenList[i];
DateTime startTime=item.BuildDate;
DateTime endTime=Convert.ToDateTime(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
TimeSpan ts=endTime - startTime;
int minutes=ts.Minutes;
if (minutes>10)
_TokenList.RemoveAt(i);
附上Helper工具類中clientId的方法:
/// 擷取并生成用戶端唯一Id,儲存在cookie中;
public static string ClientId(HttpRequestBase request) //擷取用戶端唯一Id
if (request==null)
string clientId=CookieHelper.Get("_clientId_");
if (string.IsNullOrEmpty(clientId))
string guid=Guid.NewGuid().ToString("N");//guid
string ip=StringHelper.GetClientIP().ToString().Replace(".", "").Replace(":", "");
clientId=guid + ip;
CookieHelper.Add("_clientId_", clientId);
return clientId;
/// 擷取用戶端ip
public static string GetClientIP()
if (System.Web.HttpContext.Current==null) return "127.0.0.1";
string clientIp=System.Web.HttpContext.Current.Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(clientIp))
clientIp=System.Web.HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"];
clientIp=System.Web.HttpContext.Current.Request.UserHostAddress;
if (clientIp.IndexOf(",") > 0)
clientIp=clientIp.Split(',')[0];
if (!StringHelper.IsIp(clientIp))
clientIp="127.0.0.1";
return clientIp;
前端發送方式需要更改,在請求手機驗證碼的api之前,先第一次請求
二手QQ賣号平台擷取token,然後帶上token驗證通過後後再請求api。
這個方案肯定沒有驗證碼的防護效果好,隻是增加了僞造請求的步驟,因為現在很多模拟請求的軟體都是一次性請求,是以還是能防護大部分的軟體。