From 34fa43f302ed05203d815131ceee6799ef2afa34 Mon Sep 17 00:00:00 2001 From: Abdullah Atta Date: Tue, 15 Jul 2025 13:34:31 +0500 Subject: [PATCH] global: add some basic rate limiting --- Notesnook.API/Startup.cs | 2 +- .../Controllers/AccountController.cs | 3 +++ .../Controllers/MFAController.cs | 2 ++ .../Controllers/SignupController.cs | 2 ++ Streetwriters.Identity/Services/PasswordHasher.cs | 3 +++ Streetwriters.Identity/Startup.cs | 15 +++++++++++++++ 6 files changed, 26 insertions(+), 1 deletion(-) diff --git a/Notesnook.API/Startup.cs b/Notesnook.API/Startup.cs index a43eb85..048b9ee 100644 --- a/Notesnook.API/Startup.cs +++ b/Notesnook.API/Startup.cs @@ -150,7 +150,7 @@ namespace Notesnook.API options.CacheDuration = TimeSpan.FromMinutes(30); }); - BsonSerializer.RegisterSerializer(new SyncItemBsonSerializer()); + // Serializer.RegisterSerializer(new SyncItemBsonSerializer()); if (!BsonClassMap.IsClassMapRegistered(typeof(UserSettings))) BsonClassMap.RegisterClassMap(); diff --git a/Streetwriters.Identity/Controllers/AccountController.cs b/Streetwriters.Identity/Controllers/AccountController.cs index a9b2911..d7d8093 100644 --- a/Streetwriters.Identity/Controllers/AccountController.cs +++ b/Streetwriters.Identity/Controllers/AccountController.cs @@ -31,6 +31,7 @@ using IdentityServer4.Stores; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.RateLimiting; using Streetwriters.Common; using Streetwriters.Common.Enums; using Streetwriters.Common.Interfaces; @@ -112,6 +113,7 @@ namespace Streetwriters.Identity.Controllers } [HttpPost("verify")] + [EnableRateLimiting("strict")] public async Task SendVerificationEmail([FromForm] string newEmail) { var client = Clients.FindClientById(User.FindFirstValue("client_id")); @@ -145,6 +147,7 @@ namespace Streetwriters.Identity.Controllers [HttpPost("recover")] [AllowAnonymous] + [EnableRateLimiting("strict")] public async Task ResetUserPassword([FromForm] ResetPasswordForm form) { var client = Clients.FindClientById(form.ClientId); diff --git a/Streetwriters.Identity/Controllers/MFAController.cs b/Streetwriters.Identity/Controllers/MFAController.cs index b942776..bc6c042 100644 --- a/Streetwriters.Identity/Controllers/MFAController.cs +++ b/Streetwriters.Identity/Controllers/MFAController.cs @@ -26,6 +26,7 @@ using AspNetCore.Identity.Mongo.Model; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.RateLimiting; using Streetwriters.Common; using Streetwriters.Common.Enums; using Streetwriters.Common.Models; @@ -90,6 +91,7 @@ namespace Streetwriters.Identity.Controllers [HttpPost("send")] [Authorize("mfa")] [Authorize(LocalApi.PolicyName)] + [EnableRateLimiting("strict")] public async Task RequestCode([FromForm] string type) { var client = Clients.FindClientById(User.FindFirstValue("client_id")); diff --git a/Streetwriters.Identity/Controllers/SignupController.cs b/Streetwriters.Identity/Controllers/SignupController.cs index f944b9e..2dedaf9 100644 --- a/Streetwriters.Identity/Controllers/SignupController.cs +++ b/Streetwriters.Identity/Controllers/SignupController.cs @@ -25,6 +25,7 @@ using AspNetCore.Identity.Mongo.Model; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.RateLimiting; using Streetwriters.Common; using Streetwriters.Common.Enums; using Streetwriters.Common.Models; @@ -51,6 +52,7 @@ namespace Streetwriters.Identity.Controllers [HttpPost] [AllowAnonymous] + [EnableRateLimiting("strict")] public async Task Signup([FromForm] SignupForm form) { if (Constants.DISABLE_SIGNUPS) diff --git a/Streetwriters.Identity/Services/PasswordHasher.cs b/Streetwriters.Identity/Services/PasswordHasher.cs index 83d44e6..ae6d0e4 100644 --- a/Streetwriters.Identity/Services/PasswordHasher.cs +++ b/Streetwriters.Identity/Services/PasswordHasher.cs @@ -26,8 +26,11 @@ namespace Streetwriters.Identity.Services { public class Argon2PasswordHasher : IPasswordHasher where TUser : User { + const long MAX_PASSWORD_LENGTH = 1024 * 2; public string HashPassword(TUser user, string password) { + if (password.Length > MAX_PASSWORD_LENGTH) + throw new Exception("Password is too long."); ArgumentNullException.ThrowIfNullOrEmpty(password, nameof(password)); return PasswordHelper.CreatePasswordHash(password); } diff --git a/Streetwriters.Identity/Startup.cs b/Streetwriters.Identity/Startup.cs index fc32053..2b63d57 100644 --- a/Streetwriters.Identity/Startup.cs +++ b/Streetwriters.Identity/Startup.cs @@ -19,6 +19,7 @@ along with this program. If not, see . using System; using System.IO; +using System.Threading.RateLimiting; using AspNetCore.Identity.Mongo; using IdentityServer4.MongoDB.Entities; using IdentityServer4.MongoDB.Interfaces; @@ -32,6 +33,7 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.RateLimiting; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -133,6 +135,18 @@ namespace Streetwriters.Identity options.TokenLifespan = TimeSpan.FromHours(2); }); + services.AddRateLimiter(options => + { + options.AddSlidingWindowLimiter("strict", options => + { + options.PermitLimit = 30; + options.Window = TimeSpan.FromSeconds(60); + options.SegmentsPerWindow = 10; + options.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; + options.QueueLimit = 0; + }); + }); + services.AddAuthorization(options => { options.AddPolicy("mfa", policy => @@ -199,6 +213,7 @@ namespace Streetwriters.Identity app.UseRouting(); app.UseIdentityServer(); + app.UseRateLimiter(); app.UseAuthorization(); app.UseAuthentication();