Dotnet Core Public API的安全实践
公開API的安全,其實(shí)更重要。
?
一、API的安全
作為一個(gè)Dotnet Core的老司機(jī),寫API時(shí),能兼顧到API的安全,這是一種優(yōu)雅。
?
通常,我們會(huì)用認(rèn)證來保證API的安全,無敵的Authorize能解決我們很多的問題。
但是,總有一些場合,我們沒辦法用Authorize,而只能用匿名或不加驗(yàn)證的方式來訪問。比方電商中查詢SKU的列表并在前端展示,通常這個(gè)無關(guān)用戶和權(quán)限,在完成API的時(shí)候,我們也不會(huì)加入認(rèn)證Authorize。
這種情況下,如果直接寫,不加入安全級(jí)別,這樣的體系結(jié)構(gòu)是有可能成為可供利用的安全漏洞的。
?
Dotnet Core框架已經(jīng)提供了一些常見漏洞的解決方法,包括:
跨站點(diǎn)腳本
SQL注入
跨站點(diǎn)請(qǐng)求偽造(CSRF)
重定向
等等。
但是,我們還需要更進(jìn)一步,還需要照顧到以下常見的攻擊:
拒絕服務(wù)(DOS)
分布式拒絕服務(wù)(DDOS)
批量API調(diào)用
探測(cè)響應(yīng)
數(shù)據(jù)抓取
這部分內(nèi)容,需要我們自己實(shí)現(xiàn)。當(dāng)然,這部分內(nèi)容的實(shí)現(xiàn),也可以從Web Server上進(jìn)行設(shè)置。
本文討論的,是代碼的實(shí)現(xiàn)。
二、相關(guān)代碼
今天偷個(gè)懶,不講原理,以分享代碼為主。
2.1 基于IP的客戶端請(qǐng)求限制
通過限制客戶端在指定的時(shí)間范圍內(nèi)的請(qǐng)求數(shù)量,防止惡意bot攻擊。
代碼中,我建立了一個(gè)基于IP的請(qǐng)求限制過濾器。
注意:有多個(gè)客戶端位于同一個(gè)IP地址的情況,這個(gè)情況在這個(gè)代碼中沒有考慮。如果您希望實(shí)現(xiàn)這一點(diǎn),可以把幾種方式結(jié)合起來使用。
以下是代碼:
[AttributeUsage(AttributeTargets.Method)] public?class?RequestLimitAttribute?:?ActionFilterAttribute {public?string?Name?{?get;?}public?int?NoOfRequest?{?get;?set;?}public?int?Seconds?{?get;?set;?}private?static?MemoryCache?Cache?{?get;?}?=?new?MemoryCache(new?MemoryCacheOptions());public?RequestLimitAttribute(string?name,?int?noOfRequest?=?5,?int?seconds?=?10){Name?=?name;NoOfRequest?=?noOfRequest;Seconds?=?seconds;}public?override?void?OnActionExecuting(ActionExecutingContext?context){var?ipAddress?=?context.HttpContext.Request.HttpContext.Connection.RemoteIpAddress;var?memoryCacheKey?=?$"{Name}-{ipAddress}";Cache.TryGetValue(memoryCacheKey,?out?int?prevReqCount);if?(prevReqCount?>=?NoOfRequest){context.Result?=?new?ContentResult{Content?=?$"Request?limit?is?exceeded.?Try?again?in?{Seconds}?seconds.",};context.HttpContext.Response.StatusCode?=?(int)HttpStatusCode.TooManyRequests;}else{var?cacheEntryOptions?=?new?MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromSeconds(Seconds));Cache.Set(memoryCacheKey,?(prevReqCount?+?1),?cacheEntryOptions);}} }使用時(shí),只要在需要的API前加屬性即可:
[HttpGet] [RequestLimit("DataGet",?5,?30)] public?IEnumerable<WeatherForecast>?Get() {... }2.2 引用頭檢查
對(duì)API請(qǐng)求的請(qǐng)求引用頭進(jìn)行檢查,可以防止API濫用,以及跨站點(diǎn)請(qǐng)求偽造(CSRF)攻擊。
同樣,也是采用自定義屬性的方式。
public?class?ValidateReferrerAttribute?:?ActionFilterAttribute {private?IConfiguration?_configuration;public?override?void?OnActionExecuting(ActionExecutingContext?context){_configuration?=?(IConfiguration)context.HttpContext.RequestServices.GetService(typeof(IConfiguration));base.OnActionExecuting(context);if?(!IsValidRequest(context.HttpContext.Request)){context.Result?=?new?ContentResult{Content?=?$"Invalid?referer?header",};context.HttpContext.Response.StatusCode?=?(int)HttpStatusCode.ExpectationFailed;}}private?bool?IsValidRequest(HttpRequest?request){string?referrerURL?=?"";if?(request.Headers.ContainsKey("Referer")){referrerURL?=?request.Headers["Referer"];}if?(string.IsNullOrWhiteSpace(referrerURL))?return?true;var?allowedUrls?=?_configuration.GetSection("CorsOrigin").Get<string[]>()?.Select(url?=>?new?Uri(url).Authority).ToList();bool?isValidClient?=?allowedUrls.Contains(new?Uri(referrerURL).Authority);return?isValidClient;} }這里我用了一個(gè)配置,在appsetting.json中:
{"CorsOrigin":?["https://test.com",?"http://test1.cn:8080"] }CorsOrigin參數(shù)中加入允許引用的來源域名:端口列表。
使用時(shí),在需要的API前加屬性:
[HttpGet] [ValidateReferrer] public?IEnumerable<WeatherForecast>?Get() {... }2.3 DDOS攻擊檢查
DDOS攻擊在網(wǎng)上很常見,這種攻擊簡單有效,可以讓一個(gè)網(wǎng)站瞬間開始并長時(shí)間無法響應(yīng)。通常來說,網(wǎng)站可以通過多種節(jié)流方法來避免這種情況。
下面我們換一種方式,用中間件MiddleWare來限制特定客戶端IP的請(qǐng)求數(shù)量。
public?class?DosAttackMiddleware {private?static?Dictionary<string,?short>?_IpAdresses?=?new?Dictionary<string,?short>();private?static?Stack<string>?_Banned?=?new?Stack<string>();private?static?Timer?_Timer?=?CreateTimer();private?static?Timer?_BannedTimer?=?CreateBanningTimer();private?const?int?BANNED_REQUESTS?=?10;private?const?int?REDUCTION_INTERVAL?=?1000;?//?1?second????private?const?int?RELEASE_INTERVAL?=?5?*?60?*?1000;?//?5?minutes????private?RequestDelegate?_next;public?DosAttackMiddleware(RequestDelegate?next){_next?=?next;}public?async?Task?InvokeAsync(HttpContext?httpContext){string?ip?=?httpContext.Connection.RemoteIpAddress.ToString();if?(_Banned.Contains(ip)){httpContext.Response.StatusCode?=?(int)HttpStatusCode.Forbidden;}CheckIpAddress(ip);await?_next(httpContext);}private?static?void?CheckIpAddress(string?ip){if?(!_IpAdresses.ContainsKey(ip)){_IpAdresses[ip]?=?1;}else?if?(_IpAdresses[ip]?==?BANNED_REQUESTS){_Banned.Push(ip);_IpAdresses.Remove(ip);}else{_IpAdresses[ip]++;}}private?static?Timer?CreateTimer(){Timer?timer?=?GetTimer(REDUCTION_INTERVAL);timer.Elapsed?+=?new?ElapsedEventHandler(TimerElapsed);return?timer;}private?static?Timer?CreateBanningTimer(){Timer?timer?=?GetTimer(RELEASE_INTERVAL);timer.Elapsed?+=?delegate?{if?(_Banned.Any())?_Banned.Pop();};return?timer;}private?static?Timer?GetTimer(int?interval){Timer?timer?=?new?Timer();timer.Interval?=?interval;timer.Start();return?timer;}private?static?void?TimerElapsed(object?sender,?ElapsedEventArgs?e){foreach?(string?key?in?_IpAdresses.Keys.ToList()){_IpAdresses[key]--;if?(_IpAdresses[key]?==?0)?_IpAdresses.Remove(key);}} }代碼中設(shè)置:1秒(1000ms)中有超過10次訪問時(shí),對(duì)應(yīng)的IP會(huì)被禁用5分鐘。
使用時(shí),在Startup.cs中直接加載中間件:
public?void?Configure(IApplicationBuilder?app,?IWebHostEnvironment?env) {...app.UseMiddleware<DosAttackMiddleware>();... }三、結(jié)尾的話
以上代碼僅為拋磚引玉之用。
公開的API,未經(jīng)驗(yàn)證的API,在生產(chǎn)環(huán)境會(huì)因?yàn)榉N種原因被攻擊。這幾天公司的系統(tǒng)就因?yàn)檫@個(gè)出了大事。
所以,寫API的時(shí)候,要充分考慮到這些網(wǎng)絡(luò)攻擊的可能性,通過正確的處理,來防止來自網(wǎng)絡(luò)的攻擊。
這是一份責(zé)任,也是一個(gè)理念。
與大家共勉!
?
(全文完)
?
本文的代碼,我已經(jīng)傳到Github上,位置在:https://github.com/humornif/Demo-Code/tree/master/0021/demo
喜歡就來個(gè)三連,讓更多人因你而受益
總結(jié)
以上是生活随笔為你收集整理的Dotnet Core Public API的安全实践的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 配置文件中的数据库连接串加密了,你以为我
- 下一篇: 省钱攻略送上!戴尔官网OptiPlex商