fix bug 63687: added rate limiting for api
This commit is contained in:
parent
ceb2a85979
commit
720a2817f8
@ -26,6 +26,8 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Systemd" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="7.0.1" />
|
||||||
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.3" />
|
<PackageReference Include="NLog.Web.AspNetCore" Version="5.3.3" />
|
||||||
|
<PackageReference Include="RedisRateLimiting" Version="1.0.11" />
|
||||||
|
<PackageReference Include="RedisRateLimiting.AspNetCore" Version="1.0.8" />
|
||||||
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="9.1.0" />
|
<PackageReference Include="StackExchange.Redis.Extensions.AspNetCore" Version="9.1.0" />
|
||||||
<PackageReference Include="StackExchange.Redis.Extensions.Newtonsoft" Version="9.1.0" />
|
<PackageReference Include="StackExchange.Redis.Extensions.Newtonsoft" Version="9.1.0" />
|
||||||
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||||
|
|
||||||
|
using Role = ASC.Common.Security.Authorizing.Role;
|
||||||
|
|
||||||
namespace ASC.Api.Core.Auth;
|
namespace ASC.Api.Core.Auth;
|
||||||
|
|
||||||
[Scope]
|
[Scope]
|
||||||
|
@ -100,6 +100,88 @@ public abstract class BaseStartup
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var redisOptions = _configuration.GetSection("Redis").Get<RedisConfiguration>().ConfigurationOptions;
|
||||||
|
var connectionMultiplexer = ConnectionMultiplexer.Connect(redisOptions);
|
||||||
|
|
||||||
|
services.AddRateLimiter(options =>
|
||||||
|
{
|
||||||
|
options.GlobalLimiter = PartitionedRateLimiter.CreateChained(
|
||||||
|
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
|
||||||
|
{
|
||||||
|
var userId = httpContext?.User?.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value;
|
||||||
|
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
return RateLimitPartition.GetNoLimiter("no_limiter");
|
||||||
|
}
|
||||||
|
|
||||||
|
var permitLimit = 1500;
|
||||||
|
|
||||||
|
string partitionKey;
|
||||||
|
|
||||||
|
partitionKey = $"fw_{userId}";
|
||||||
|
|
||||||
|
return RedisRateLimitPartition.GetFixedWindowRateLimiter(partitionKey, key => new RedisFixedWindowRateLimiterOptions
|
||||||
|
{
|
||||||
|
PermitLimit = permitLimit,
|
||||||
|
Window = TimeSpan.FromMinutes(1),
|
||||||
|
ConnectionMultiplexerFactory = () => connectionMultiplexer
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
PartitionedRateLimiter.Create<HttpContext, string>(httpContext =>
|
||||||
|
{
|
||||||
|
var userId = httpContext?.User?.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value;
|
||||||
|
string partitionKey;
|
||||||
|
int permitLimit;
|
||||||
|
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
return RateLimitPartition.GetNoLimiter("no_limiter");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.Compare(httpContext.Request.Method, "GET", true) == 0)
|
||||||
|
{
|
||||||
|
permitLimit = 50;
|
||||||
|
partitionKey = $"cr_read_{userId}";
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
permitLimit = 15;
|
||||||
|
partitionKey = $"cr_write_{userId}";
|
||||||
|
}
|
||||||
|
|
||||||
|
return RedisRateLimitPartition.GetConcurrencyRateLimiter(partitionKey, key => new RedisConcurrencyRateLimiterOptions
|
||||||
|
{
|
||||||
|
PermitLimit = permitLimit,
|
||||||
|
QueueLimit = 0,
|
||||||
|
ConnectionMultiplexerFactory = () => connectionMultiplexer
|
||||||
|
});
|
||||||
|
}
|
||||||
|
));
|
||||||
|
|
||||||
|
options.AddPolicy("sensitive_api", httpContext => {
|
||||||
|
var userId = httpContext?.User?.Claims.FirstOrDefault(x => x.Type == ClaimTypes.Sid)?.Value;
|
||||||
|
|
||||||
|
if (userId == null)
|
||||||
|
{
|
||||||
|
return RateLimitPartition.GetNoLimiter("no_limiter");
|
||||||
|
}
|
||||||
|
|
||||||
|
var permitLimit = 5;
|
||||||
|
var partitionKey = $"sensitive_api_{userId}";
|
||||||
|
|
||||||
|
return RedisRateLimitPartition.GetFixedWindowRateLimiter(partitionKey, key => new RedisFixedWindowRateLimiterOptions
|
||||||
|
{
|
||||||
|
PermitLimit = permitLimit,
|
||||||
|
Window = TimeSpan.FromMinutes(1),
|
||||||
|
ConnectionMultiplexerFactory = () => connectionMultiplexer
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
options.OnRejected = (context, ct) => RateLimitMetadata.OnRejected(context.HttpContext, context.Lease, ct);
|
||||||
|
});
|
||||||
|
|
||||||
services.AddScoped<EFLoggerFactory>();
|
services.AddScoped<EFLoggerFactory>();
|
||||||
|
|
||||||
services.AddBaseDbContextPool<AccountLinkContext>()
|
services.AddBaseDbContextPool<AccountLinkContext>()
|
||||||
@ -311,6 +393,8 @@ public abstract class BaseStartup
|
|||||||
|
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
|
|
||||||
|
app.UseRateLimiter();
|
||||||
|
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
|
|
||||||
app.UseCultureMiddleware();
|
app.UseCultureMiddleware();
|
||||||
@ -346,6 +430,8 @@ public abstract class BaseStartup
|
|||||||
await context.Response.WriteAsync($"{Environment.MachineName} running {CustomHealthCheck.Running}");
|
await context.Response.WriteAsync($"{Environment.MachineName} running {CustomHealthCheck.Running}");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConfigureContainer(ContainerBuilder builder)
|
public void ConfigureContainer(ContainerBuilder builder)
|
||||||
|
@ -41,6 +41,7 @@ global using System.Text.Encodings.Web;
|
|||||||
global using System.Text.Json;
|
global using System.Text.Json;
|
||||||
global using System.Text.Json.Serialization;
|
global using System.Text.Json.Serialization;
|
||||||
global using System.Text.RegularExpressions;
|
global using System.Text.RegularExpressions;
|
||||||
|
global using System.Threading.RateLimiting;
|
||||||
global using System.Web;
|
global using System.Web;
|
||||||
global using System.Xml.Linq;
|
global using System.Xml.Linq;
|
||||||
|
|
||||||
@ -150,6 +151,10 @@ global using NLog.Web;
|
|||||||
|
|
||||||
global using RabbitMQ.Client;
|
global using RabbitMQ.Client;
|
||||||
|
|
||||||
|
global using RedisRateLimiting;
|
||||||
|
global using RedisRateLimiting.AspNetCore;
|
||||||
|
|
||||||
|
global using StackExchange.Redis;
|
||||||
global using StackExchange.Redis.Extensions.Core.Configuration;
|
global using StackExchange.Redis.Extensions.Core.Configuration;
|
||||||
global using StackExchange.Redis.Extensions.Newtonsoft;
|
global using StackExchange.Redis.Extensions.Newtonsoft;
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@
|
|||||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.RateLimiting;
|
||||||
|
|
||||||
namespace ASC.People.Api;
|
namespace ASC.People.Api;
|
||||||
|
|
||||||
public class UserController : PeopleControllerBase
|
public class UserController : PeopleControllerBase
|
||||||
@ -1222,6 +1224,7 @@ public class UserController : PeopleControllerBase
|
|||||||
[AllowNotPayment]
|
[AllowNotPayment]
|
||||||
[AllowAnonymous]
|
[AllowAnonymous]
|
||||||
[HttpPost("password")]
|
[HttpPost("password")]
|
||||||
|
[EnableRateLimiting("sensitive_api")]
|
||||||
public async Task<object> SendUserPasswordAsync(MemberRequestDto inDto)
|
public async Task<object> SendUserPasswordAsync(MemberRequestDto inDto)
|
||||||
{
|
{
|
||||||
if (_authContext.IsAuthenticated)
|
if (_authContext.IsAuthenticated)
|
||||||
|
Loading…
Reference in New Issue
Block a user