/// <summary> /// Is this a dos attack? /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) IsDosAttack(AntiDosFirewallRequestInfo requestInfo) { var key = GetCacheKey(requestInfo); var expiresAt = GetCacheExpiresAt(); if (!_cacheService.TryGetValue <ThrottleInfo>(key, out var clientThrottleInfo)) { clientThrottleInfo = new ThrottleInfo { RequestsCount = 1, ExpiresAt = expiresAt }; _cacheService.Add(key, clientThrottleInfo, expiresAt, size: 1); return(false, clientThrottleInfo); } if (clientThrottleInfo.RequestsCount > _antiDosConfig.Value.AllowedRequests) { clientThrottleInfo.BanReason = "IsDosAttack"; _cacheService.Add(key, clientThrottleInfo, expiresAt, size: 1); return(true, clientThrottleInfo); } clientThrottleInfo.RequestsCount++; _cacheService.Add(key, clientThrottleInfo, expiresAt, size: 1); return(false, clientThrottleInfo); }
/// <summary> /// Should block client based on its IP? /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) ShouldBanIp(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } if (_antiDosConfig.Value.BannedIPAddressRanges?.Any() != true) { return(false, null); } if (requestInfo.IP == null) { return(false, null); } var iPAddress = IPAddress.Parse(requestInfo.IP); foreach (var range in _antiDosConfig.Value.BannedIPAddressRanges) { var ipRange = IPAddressRange.Parse(range); if (ipRange.Contains(iPAddress)) { return(true, new ThrottleInfo { ExpiresAt = GetCacheExpiresAt(), RequestsCount = _antiDosConfig.Value.AllowedRequests, BanReason = $"IP: {requestInfo.IP} is in the `{range}` range." }); } } return(false, null); }
/// <summary> /// Returns cache's key /// </summary> public string GetCacheKey(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } return($"__AntiDos__{requestInfo.IP}"); }
/// <summary> /// Should block client based on its info? /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) ShouldBlockClient(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } var shouldBanIpResult = ShouldBanIp(requestInfo); if (shouldBanIpResult.ShouldBlockClient) { return(true, shouldBanIpResult.ThrottleInfo); } if (_antiDosConfig.Value.IgnoreLocalHost && requestInfo.IsLocal) { return(false, null); } var hasUrlAttackVectorsResult = HasUrlAttackVectors(requestInfo); if (hasUrlAttackVectorsResult.ShouldBlockClient) { return(true, hasUrlAttackVectorsResult.ThrottleInfo); } var isBadBotResult = IsBadBot(requestInfo); if (isBadBotResult.ShouldBlockClient) { return(true, isBadBotResult.ThrottleInfo); } if (IsGoodBot(requestInfo)) { return(false, null); } var isDosAttackResult = IsDosAttack(requestInfo); if (isDosAttackResult.ShouldBlockClient) { return(true, isDosAttackResult.ThrottleInfo); } return(false, null); }
/// <summary> /// Such as `asafaweb`. /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) IsBadBot(AntiDosFirewallRequestInfo requestInfo) { var shouldBanBotHeader = ShouldBanBotHeaders(requestInfo); if (shouldBanBotHeader.ShouldBlockClient) { return(true, shouldBanBotHeader.ThrottleInfo); } var shouldBanUserAgent = ShouldBanUserAgent(requestInfo); if (shouldBanUserAgent.ShouldBlockClient) { return(true, shouldBanUserAgent.ThrottleInfo); } return(false, null); }
/// <summary> /// Is from remote localhost /// </summary> public bool?IsFromRemoteLocalhost(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } if (requestInfo.UrlReferrer == null) { return(false); } if (!requestInfo.UrlReferrer.Host.Contains("localhost", StringComparison.OrdinalIgnoreCase)) { return(false); } return(!requestInfo.IP?.IsLocalIp()); }
/// <summary> /// Such as `google` or `bing`. /// </summary> public bool IsGoodBot(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } if (string.IsNullOrWhiteSpace(requestInfo.UserAgent)) { return(false); } if (_antiDosConfig.Value.GoodBotsUserAgents?.Any() != true) { return(true); } return(_antiDosConfig.Value.GoodBotsUserAgents .Any(goodBot => requestInfo.UserAgent.IndexOf(goodBot, StringComparison.OrdinalIgnoreCase) != -1)); }
/// <summary> /// Such as `asafaweb`. /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) ShouldBanUserAgent(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } if (string.IsNullOrWhiteSpace(requestInfo.UserAgent)) { return(false, null); // for ping-backs validations } if (_antiDosConfig.Value.BadBotsUserAgents?.Any() != true) { return(false, null); } var userAgent = _antiDosConfig.Value.BadBotsUserAgents .FirstOrDefault(badBot => requestInfo.UserAgent.IndexOf(badBot, StringComparison.OrdinalIgnoreCase) != -1); if (!string.IsNullOrWhiteSpace(userAgent)) { return(true, new ThrottleInfo { ExpiresAt = GetCacheExpiresAt(), RequestsCount = _antiDosConfig.Value.AllowedRequests, BanReason = $"BadBotUserAgent: {userAgent}." }); } var isLocal = IsFromRemoteLocalhost(requestInfo); if (isLocal == true) { return(true, new ThrottleInfo { ExpiresAt = GetCacheExpiresAt(), RequestsCount = _antiDosConfig.Value.AllowedRequests, BanReason = $"IsFromRemoteLocalhost." }); } return(false, null); }
/// <summary> /// Such as `HTTP_ACUNETIX_PRODUCT`. /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) ShouldBanBotHeaders(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } if (_antiDosConfig.Value.BadBotsRequestHeaders?.Any() != true || requestInfo.RequestHeaders == null) { return(false, null); } foreach (var headerkey in requestInfo.RequestHeaders.Keys) { var headerValue = requestInfo.RequestHeaders[headerkey]; if (string.IsNullOrWhiteSpace(headerValue)) { continue; } var botHeader = _antiDosConfig.Value.BadBotsRequestHeaders .FirstOrDefault(badBotHeader => headerValue.ToString().IndexOf(badBotHeader, StringComparison.OrdinalIgnoreCase) != -1 || headerkey.IndexOf(badBotHeader, StringComparison.OrdinalIgnoreCase) != -1); if (!string.IsNullOrWhiteSpace(botHeader)) { return(true, new ThrottleInfo { ExpiresAt = GetCacheExpiresAt(), RequestsCount = _antiDosConfig.Value.AllowedRequests, BanReason = $"BadBotRequestHeader: {botHeader}." }); } } return(false, null); }
/// <summary> /// Such as `HTTP_ACUNETIX_PRODUCT`. /// </summary> public (bool ShouldBlockClient, ThrottleInfo?ThrottleInfo) HasUrlAttackVectors(AntiDosFirewallRequestInfo requestInfo) { if (requestInfo == null) { throw new ArgumentNullException(nameof(requestInfo)); } if (string.IsNullOrWhiteSpace(requestInfo.RawUrl)) { return(false, null); } if (requestInfo.RawUrl.EndsWith(".php", StringComparison.OrdinalIgnoreCase)) { return(true, new ThrottleInfo { ExpiresAt = GetCacheExpiresAt(), RequestsCount = _antiDosConfig.Value.AllowedRequests, BanReason = $"{requestInfo.RawUrl} ends with .php and this an ASP.NET Core site!" }); } if (_antiDosConfig.Value.UrlAttackVectors?.Any() != true) { return(false, null); } var vector = _antiDosConfig.Value.UrlAttackVectors .FirstOrDefault(attackVector => requestInfo.RawUrl.IndexOf(attackVector, StringComparison.OrdinalIgnoreCase) != -1); if (!string.IsNullOrWhiteSpace(vector)) { return(true, new ThrottleInfo { ExpiresAt = GetCacheExpiresAt(), RequestsCount = _antiDosConfig.Value.AllowedRequests, BanReason = $"UrlAttackVector: {vector}." }); } return(false, null); }