public void TestOriginBlacklisting() { var key = GetRandomKey(); var ip = GetRandomIP(); var interval = TimeSpan.FromMinutes(1); Assert.AreEqual( false, HackerSprayer.isOriginBlacklistedAsync(ip).Run(), "Origin must not be blacklisted"); var startTime = DateTime.Now; HackerSprayer.BlacklistOriginAsync(ip, interval).Run(); Assert.AreEqual( true, HackerSprayer.isOriginBlacklistedAsync(ip).Run(), "Origin must be blacklisted"); WaitForIntervalToElapse(interval, startTime); Assert.AreEqual( false, HackerSprayer.isOriginBlacklistedAsync(ip).Run(), "Origin must not be blacklisted after expiration time"); }
public void TestKeyBlacklistingAndWhitelisting() { var ip = GetRandomIP(); var key = GetRandomKey(); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip).Run(), string.Format("Allow traffic on {0} from {1}", key, ip)); HackerSprayer.BlacklistKeyAsync(key, TimeSpan.FromMinutes(5)).Run(); Assert.AreEqual( HackerSprayer.Result.KeyBlocked, HackerSprayer.DefendAsync(key, ip).Run(), string.Format("Block traffic on {0} from {1}", key, ip)); Assert.AreEqual( HackerSprayer.Result.KeyBlocked, HackerSprayer.DefendAsync(key, GetRandomIP()).Run(), string.Format("Allow any traffic from {1}", key, ip)); HackerSprayer.WhitelistKeyAsync(key).Run(); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip).Run(), string.Format("Allow traffic on {0} from {1} after whitelist", key, ip)); }
public void TestKeyBlacklisting() { var key = GetRandomKey(); var ip = GetRandomIP(); var interval = TimeSpan.FromMinutes(1); Assert.AreEqual( false, HackerSprayer.IsKeyBlacklistedAsync(key).Run(), "Key must not be blacklisted"); var startTime = DateTime.Now; HackerSprayer.BlacklistKeyAsync(key, interval).Run(); Assert.AreEqual( true, HackerSprayer.IsKeyBlacklistedAsync(key).Run(), "Key must be blacklisted"); string [] blacklistedKeys = HackerSprayer.GetKeyBlacklists().Run(); Assert.IsTrue(Array.Exists(blacklistedKeys, k => k == key), "Key must be in the blacklist"); WaitForIntervalToElapse(interval, startTime); Assert.AreEqual( false, HackerSprayer.IsKeyBlacklistedAsync(key).Run(), "Key must not be blacklisted after expiration time"); }
public void TestOriginBlaclistingAndWhitelisting() { var ip = GetRandomIP(); var key = GetRandomKey(); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip).Run(), string.Format("Allow traffic on {0} from {1}", key, ip)); HackerSprayer.BlacklistOriginAsync(ip).Run(); Assert.AreEqual( HackerSprayer.Result.OriginBlocked, HackerSprayer.DefendAsync(key, ip).Run(), string.Format("Block traffic on {0} from {1}", key, ip)); Assert.AreEqual( HackerSprayer.Result.OriginBlocked, HackerSprayer.DefendAsync(GetRandomKey(), ip).Run(), string.Format("Allow any traffic from {1}", key, ip)); HackerSprayer.WhitelistOriginAsync(ip).Run(); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip).Run(), string.Format("Allow traffic on {0} from {1} after whitelist", key, ip)); }
public async Task <IActionResult> ClearAllBlocks() { await HackerSprayer.ClearAllHitsAsync(); await HackerSprayer.ClearBlacklistsAsync(); return(await LogOff()); }
//[ValidateAntiForgeryToken] public async Task <IActionResult> Login(LoginViewModel model, string returnUrl = null) { // This handles load balancers passing the original client IP // through this header. // WARNING: If you load balancer is not passing original client IP // through this header, then you will be blocking your load balancer, // causing a total outage. Also ensure this Header cannot be spoofed. var originIP = Request.Headers.ContainsKey("X-Forward-For") ? IPAddress.Parse(Request.Headers["X-Forward-For"]).MapToIPv4() : Request.HttpContext.Connection.RemoteIpAddress.MapToIPv4(); return(await HackerSprayer.DefendAsync <IActionResult>(async (success, fail) => { // Don't forget to do this check! We use model.Email for key. if (string.IsNullOrWhiteSpace(model.Email) || string.IsNullOrWhiteSpace(model.Password)) { return await fail(View(model)); } ViewData["ReturnUrl"] = returnUrl; if (ModelState.IsValid) { // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { _logger.LogInformation(1, "User logged in."); return await success(RedirectToLocal(returnUrl)); } if (result.RequiresTwoFactor) { return await success(RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe })); } if (result.IsLockedOut) { _logger.LogWarning(2, "User account locked out."); return await fail(View("Lockout")); } else { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return await fail(View(model)); } } // If we got this far, something failed, redisplay form return await fail(View(model)); }, blocked => new StatusCodeResult((int)HttpStatusCode.Forbidden), "ValidLogin:"******"InvalidLogin:" + model.Email, 3, TimeSpan.FromMinutes(15), originIP)); }
//[ValidateAntiForgeryToken] public async Task <IActionResult> Login(LoginViewModel model, string returnUrl = null) { SignInResult result = default(SignInResult); // This handles load balancers passing the original client IP // through this header. // WARNING: If you load balancer is not passing original client IP // through this header, then you will be blocking your load balancer, // causing a total outage. Also ensure this Header cannot be spoofed. var originIP = Request.Headers["X-Forward-For"] == null?Context.Connection.RemoteIpAddress.MapToIPv4() : IPAddress.Parse(Request.Headers["X-Forward-For"]).MapToIPv4(); return(await HackerSprayer.DefendAsync <IActionResult>(async (success, fail) => { ViewData["ReturnUrl"] = returnUrl; if (ModelState.IsValid) { // This doesn't count login failures towards account lockout // To enable password failures to trigger account lockout, set lockoutOnFailure: true result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false); if (result.Succeeded) { return await success(RedirectToLocal(returnUrl)); } if (result.RequiresTwoFactor) { return await success(RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe })); } if (result.IsLockedOut) { return await fail(View("Lockout")); } else { ModelState.AddModelError(string.Empty, "Invalid login attempt."); return await fail(View(model)); } } else { return await fail(View(model)); } }, blocked => new HttpStatusCodeResult(405), "ValidLogin:"******"InvalidLogin:" + model.Email, MaxInvalidLogin, MaxInvalidLoginInterval, originIP )); }
public void TestKeyCustomHitLimit() { HackerSprayer.Config.MaxHitsPerKey = 100; HackerSprayer.Config.MaxHitsPerOrigin = 100; HackerSprayer.Config.MaxHitsPerKeyPerOrigin = 100; var key = "InvalidLogin-" + GetRandomKey(); var ip = GetRandomIP(); var interval = TimeSpan.FromMinutes(1); var maxHits = 10; var startTime = DateTime.Now; Parallel.For(0, maxHits, hit => { Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip, interval, maxHits, TimeSpan.MaxValue, long.MaxValue, TimeSpan.MaxValue, long.MaxValue).Run(), "Allow hits on key for custom interval"); }); Assert.AreEqual( HackerSprayer.Result.TooManyHitsOnKey, HackerSprayer.DefendAsync(key, ip, interval, maxHits, TimeSpan.MaxValue, long.MaxValue, TimeSpan.MaxValue, long.MaxValue).Run(), "Must not allow hits on key after custom interval"); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync("InvalidLogin-" + GetRandomKey(), ip, interval, maxHits, TimeSpan.MaxValue, long.MaxValue, TimeSpan.MaxValue, long.MaxValue).Run(), "Allow hits on different key from same IP"); WaitForIntervalToElapse(interval, startTime); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip, interval, maxHits, TimeSpan.MaxValue, long.MaxValue, TimeSpan.MaxValue, long.MaxValue).Run(), "Allow hits on key for after interval has passed."); }
public void TestMaxHitsPerOrigin() { HackerSprayer.Config.MaxHitsPerKey = 20; HackerSprayer.Config.MaxHitsPerOrigin = 10; HackerSprayer.Config.MaxHitsPerKeyPerOrigin = 20; var ip = GetRandomIP(); Func <string> keyGenerator = () => { return("TestMaxHitsPerOrigin" + GetRandomKey()); }; var startTime = DateTime.Now; var result = Parallel.For(0, HackerSprayer.Config.MaxHitsPerOrigin, hit => { Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(keyGenerator(), ip).Run() ); }); while (!result.IsCompleted) { Thread.Sleep(100); } Assert.AreEqual( HackerSprayer.Config.MaxHitsPerOrigin, HackerSprayer.GetHitsFromOrigin(ip).Run(), "Number of hits recorded must match"); // No more requests from same IP Assert.AreEqual( HackerSprayer.Result.TooManyHitsFromOrigin, HackerSprayer.DefendAsync(keyGenerator(), ip).Run() ); // Allow requests from other IPs Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(keyGenerator(), GetRandomIP()).Run()); WaitForIntervalToElapse(HackerSprayer.Config.MaxHitsPerOriginInterval, startTime); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(keyGenerator(), ip).Run(), "Allow hits from same origin after expiration time." ); }
public void TestMaxHitsOnKeyPerOrigin() { HackerSprayer.Config.MaxHitsPerKey = 20; HackerSprayer.Config.MaxHitsPerOrigin = 20; HackerSprayer.Config.MaxHitsPerKeyPerOrigin = 10; Func <string> keyGenerator = () => { return("TestMaxHitsOnKeyPerOrigin" + GetRandomKey()); }; var key = keyGenerator(); var ip = GetRandomIP(); var startTime = DateTime.Now; Parallel.For(0, HackerSprayer.Config.MaxHitsPerKeyPerOrigin, hit => { Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip).Run(), "Allow hits on same key and IP"); }); Assert.AreEqual( HackerSprayer.Config.MaxHitsPerKeyPerOrigin, HackerSprayer.GetHitsForKey(key).Run(), "Number of hits recorded must match"); // No more requests from same key and IP Assert.AreEqual( HackerSprayer.Result.TooManyHitsOnKeyFromOrigin, HackerSprayer.DefendAsync(key, ip).Run()); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(keyGenerator(), ip).Run(), "From different key, same IP, allow"); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, GetRandomIP()).Run(), "From different IP, same key, allow"); WaitForIntervalToElapse(HackerSprayer.Config.MaxHitsPerOriginInterval, startTime); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(key, ip).Run(), "Allow hits on same key and IP after expiration"); }
public void TestMaxHitsPerKey() { HackerSprayer.Config.MaxHitsPerKey = 10; HackerSprayer.Config.MaxHitsPerOrigin = 20; HackerSprayer.Config.MaxHitsPerKeyPerOrigin = 20; Func <string> keyGenerator = () => { return("TestMaxHitsPerKey" + GetRandomKey()); }; var fixedKey = keyGenerator(); var startTime = DateTime.Now; Parallel.For(0, HackerSprayer.Config.MaxHitsPerKey, hit => { Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(fixedKey, GetRandomIP()).Run() ); }); Assert.AreEqual( HackerSprayer.Config.MaxHitsPerKey, HackerSprayer.GetHitsForKey(fixedKey).Run(), "Number of hits recorded must match"); Assert.AreEqual( HackerSprayer.Result.TooManyHitsOnKey, HackerSprayer.DefendAsync(fixedKey, GetRandomIP()).Run()); var ip = GetRandomIP(); Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(keyGenerator(), ip).Run(), "Allow traffic from aonther key on same IP"); WaitForIntervalToElapse(HackerSprayer.Config.MaxHitsPerKeyInterval, startTime); // Hit from another IP using same key should be allowed Assert.AreEqual( HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync(fixedKey, GetRandomIP()).Run(), "After expiration time, key must be unblocked"); }
public async Task <ActionResult> LogOn(string username, string password) { // This handles load balancers passing the original client IP // through this header. // WARNING: If you load balancer is not passing original client IP // through this header, then you will be blocking your load balancer, // causing a total outage. Also ensure this Header cannot be spoofed. var originIP = IPAddress.Parse(Request.Headers["X-Forward-For"] ?? Request.UserHostAddress).MapToIPv4(); // Don't forget to do this check! if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) { TempData[TempDataConstants.ERROR_MESSAGE] = "Invalid username or password"; return(View("~/Views/Account/LogOn.cshtml")); } return(await HackerSprayer.DefendAsync <ActionResult>(async (success, fail) => { var user = DataStore.Users.Where(u => u.Username == username && u.Password == password).FirstOrDefault(); if (user != null) { Session[SessionConstants.USER] = user; return await success(RedirectToAction("Index", "Home")); } else { TempData[TempDataConstants.ERROR_MESSAGE] = "Invalid username or password"; return await fail(View("~/Views/Account/LogOn.cshtml")); } }, blocked => new HttpStatusCodeResult(HttpStatusCode.Forbidden), "ValidLogin:"******"InvalidLogin:" + username, MaxInvalidLogin, MaxInvalidLoginInterval, originIP )); }
public void TestAllowed() { var result = HackerSprayer.DefendAsync("TestAllowed" + GetRandomKey(), GetRandomIP()).Run(); Assert.AreEqual(HackerSprayer.Result.Allowed, result); }
public void Cleanup() { HackerSprayer.ClearAllHitsAsync().Run(); HackerSprayer.ClearBlacklistsAsync().Run(); HackerSprayer.Store.Dispose(); }
public void TestOriginRangeBlocking() { HackerSprayer.ClearBlacklistsAsync().Run(); HackerSprayer.ClearAllHitsAsync().Run(); var ipsInRange = new[] { IPAddress.Parse("10.10.10.10"), IPAddress.Parse("10.10.10.11"), IPAddress.Parse("10.10.254.254"), IPAddress.Parse("10.11.10.9"), IPAddress.Parse("10.11.10.10"), IPAddress.Parse("9.1.1.1"), IPAddress.Parse("9.1.1.10"), IPAddress.Parse("9.10.10.9"), IPAddress.Parse("10.11.10.12"), IPAddress.Parse("127.254.254.254"), IPAddress.Parse("100.100.100.100"), IPAddress.Parse("128.10.10.12"), IPAddress.Parse("128.10.10.254"), IPAddress.Parse("128.10.10.128"), }; var ipsOutofRange = new[] { IPAddress.Parse("10.10.10.9"), IPAddress.Parse("9.10.10.10"), IPAddress.Parse("10.11.10.11"), IPAddress.Parse("128.10.10.11"), IPAddress.Parse("200.200.200.200"), IPAddress.Parse("1.1.1.1"), IPAddress.Parse("10.0.0.0") }; HackerSprayer.BlacklistOriginAsync(IPAddress.Parse("10.10.10.10"), IPAddress.Parse("10.11.10.10")).Run(); HackerSprayer.BlacklistOriginAsync(IPAddress.Parse("9.1.1.1"), IPAddress.Parse("9.10.10.9")).Run(); HackerSprayer.BlacklistOriginAsync(IPAddress.Parse("10.11.10.12"), IPAddress.Parse("127.254.254.254")).Run(); HackerSprayer.BlacklistOriginAsync(IPAddress.Parse("128.10.10.12"), IPAddress.Parse("128.10.10.254")).Run(); Array.ForEach(ipsInRange, ip => Assert.AreEqual(HackerSprayer.Result.OriginBlocked, HackerSprayer.DefendAsync("TestOriginRangeBlocking", ip).Run(), ip.ToString() + " must be blocked.")); HackerSprayer.ClearAllHitsAsync().Run(); Array.ForEach(ipsOutofRange, ip => Assert.AreEqual(HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync("TestOriginRangeBlocking", ip).Run(), ip.ToString() + " must be allowed")); HackerSprayer.WhitelistOriginAsync(IPAddress.Parse("9.1.1.1"), IPAddress.Parse("9.10.10.9")).Run(); Array.ForEach(new[] { IPAddress.Parse("9.1.1.1"), IPAddress.Parse("9.1.1.10"), IPAddress.Parse("9.10.10.9") }, ip => Assert.AreEqual(HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync("TestOriginRangeBlocking", ip).Run(), ip.ToString() + " must be allowed")); HackerSprayer.ClearBlacklistsAsync().Run(); HackerSprayer.ClearAllHitsAsync().Run(); Array.ForEach(ipsInRange, ip => Assert.AreEqual(HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync("TestOriginRangeBlocking", ip).Run(), ip.ToString() + " must be allowed when there's no blacklisting.")); HackerSprayer.ClearAllHitsAsync().Run(); Array.ForEach(ipsOutofRange, ip => Assert.AreEqual(HackerSprayer.Result.Allowed, HackerSprayer.DefendAsync("TestOriginRangeBlocking", ip).Run(), ip.ToString() + " must be allowed when there's no blacklisting")); }
public static async Task <HackerSprayer.Result> DefendURL(HttpContext context) { HackerSprayer.Result result = HackerSprayer.Result.Allowed; if (!Initialized) { lock (lockObject) { if (!Initialized) { Initialized = true; HackerSprayer.Store = new RedisDefenceStore(HackerSprayConfig.Settings.Redis, HackerSprayConfig.Settings.Prefix, HackerSprayer.Config); } } } // This handles load balancers passing the original client IP // through this header. // WARNING: If your load balancer is not passing original client IP // through this header, then you will be blocking your load balancer, // causing a total outage. Also ensure this Header cannot be spoofed. // Your load balancer should be configured in a way that it does not accept // this header from the request, instead it always sets it itself. var originIP = IPAddress.Parse(context.Request.Headers["X-Forward-For"] ?? context.Request.UserHostAddress).MapToIPv4(); foreach (var path in HackerSprayConfig.Settings.Paths) { if ((path.Post && context.Request.HttpMethod == "POST") || (!path.Post && context.Request.HttpMethod == "GET") && path.Name == context.Request.Path) { if (path.Mode == "perkey") { result = await HackerSprayer.DefendAsync(context.Request.Path, originIP, path.Interval, path.MaxAttempts, TimeSpan.MaxValue, long.MaxValue, TimeSpan.MaxValue, long.MaxValue); } else if (path.Mode == "perorigin") { result = await HackerSprayer.DefendAsync(context.Request.Path, originIP, TimeSpan.MaxValue, long.MaxValue, path.Interval, path.MaxAttempts, TimeSpan.MaxValue, long.MaxValue); } else //(path.Mode == "perkeyorigin") { result = await HackerSprayer.DefendAsync(context.Request.Path, originIP, TimeSpan.MaxValue, long.MaxValue, TimeSpan.MaxValue, long.MaxValue, path.Interval, path.MaxAttempts); } // Blacklist origin. After that, it becomes least expensive to block requests if (result == HackerSprayer.Result.TooManyHitsFromOrigin) { await HackerSprayer.BlacklistOriginAsync(originIP, path.Interval); } else if (result == HackerSprayer.Result.TooManyHitsOnKey) { await HackerSprayer.BlacklistKeyAsync(path.Name, path.Interval); } //else if (result == HackerSprayer.Result.TooManyHitsOnKeyFromOrigin) // There is nothing for that, because we neither want to block the key for all ip, // nor do we want to block the ip for all key. It just has to be checked each // and every time. Thus this is the most expensive check. break; } } return(result); }