Add spam rate limits for public invoice endpoints (Fix #3782) (#3889)

This commit is contained in:
Nicolas Dorier 2022-06-21 12:33:20 +09:00 committed by GitHub
parent 9d41a52d3b
commit 0aa7dacbca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 33 additions and 30 deletions

View File

@ -1,4 +1,4 @@
 <Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="../Build/Version.csproj" Condition="Exists('../Build/Version.csproj')" />
<Import Project="../Build/Common.csproj" />
@ -71,7 +71,7 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="NicolasDorier.CommandLine" Version="1.0.0.2" />
<PackageReference Include="NicolasDorier.CommandLine.Configuration" Version="1.0.0.3" />
<PackageReference Include="NicolasDorier.RateLimits" Version="1.1.0" />
<PackageReference Include="NicolasDorier.RateLimits" Version="1.2.3" />
<PackageReference Include="NicolasDorier.StandardConfiguration" Version="1.0.0.18" />
<PackageReference Include="Serilog" Version="2.9.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />

View File

@ -35,7 +35,7 @@ namespace BTCPayServer.Controllers.Greenfield
private readonly SettingsRepository _settingsRepository;
private readonly EventAggregator _eventAggregator;
private readonly IPasswordValidator<ApplicationUser> _passwordValidator;
private readonly RateLimitService _throttleService;
private readonly IRateLimitService _throttleService;
private readonly BTCPayServerOptions _options;
private readonly IAuthorizationService _authorizationService;
private readonly UserService _userService;
@ -46,7 +46,7 @@ namespace BTCPayServer.Controllers.Greenfield
PoliciesSettings policiesSettings,
EventAggregator eventAggregator,
IPasswordValidator<ApplicationUser> passwordValidator,
RateLimitService throttleService,
IRateLimitService throttleService,
BTCPayServerOptions options,
IAuthorizationService authorizationService,
UserService userService,

View File

@ -18,6 +18,7 @@ using Microsoft.AspNetCore.Http.Extensions;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using NBitpayClient;
using NicolasDorier.RateLimits;
using static BTCPayServer.Controllers.UIAppsController;
namespace BTCPayServer.Controllers
@ -116,6 +117,7 @@ namespace BTCPayServer.Controllers
[IgnoreAntiforgeryToken]
[EnableCors(CorsPolicies.All)]
[DomainMappingConstraint(AppType.PointOfSale)]
[RateLimitsFilter(ZoneLimits.PublicInvoices, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> ViewPointOfSale(string appId,
PosViewType viewType,
[ModelBinder(typeof(InvariantDecimalModelBinder))] decimal? amount,
@ -292,6 +294,7 @@ namespace BTCPayServer.Controllers
[IgnoreAntiforgeryToken]
[EnableCors(CorsPolicies.All)]
[DomainMappingConstraintAttribute(AppType.Crowdfund)]
[RateLimitsFilter(ZoneLimits.PublicInvoices, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> ContributeToCrowdfund(string appId, ContributeToCrowdfund request, CancellationToken cancellationToken)
{

View File

@ -10,6 +10,7 @@ using BTCPayServer.Plugins.PayButton.Models;
using BTCPayServer.Services.Stores;
using Microsoft.AspNetCore.Cors;
using Microsoft.AspNetCore.Mvc;
using NicolasDorier.RateLimits;
namespace BTCPayServer.Controllers
{
@ -38,6 +39,7 @@ namespace BTCPayServer.Controllers
[Route("api/v1/invoices")]
[IgnoreAntiforgeryToken]
[EnableCors(CorsPolicies.All)]
[RateLimitsFilter(ZoneLimits.PublicInvoices, Scope = RateLimitsScope.RemoteAddress)]
public async Task<IActionResult> PayButtonHandle([FromForm] PayButtonViewModel model, CancellationToken cancellationToken)
{
var store = await _StoreRepository.FindStore(model.StoreId);

View File

@ -437,28 +437,7 @@ namespace BTCPayServer.Hosting
{
options.AddPolicy(CorsPolicies.All, p => p.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
});
services.AddSingleton(provider =>
{
var btcPayEnv = provider.GetService<BTCPayServerEnvironment>();
var rateLimits = new RateLimitService();
if (btcPayEnv.IsDeveloping)
{
rateLimits.SetZone($"zone={ZoneLimits.Login} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Register} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PayJoin} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Shopify} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.ForgotPassword} rate=5r/d burst=3 nodelay");
}
else
{
rateLimits.SetZone($"zone={ZoneLimits.Login} rate=5r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Register} rate=2r/min burst=2 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PayJoin} rate=5r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Shopify} rate=20r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.ForgotPassword} rate=5r/d burst=5 nodelay");
}
return rateLimits;
});
services.AddRateLimits();
services.AddLogging(logBuilder =>
{
var debugLogFile = BTCPayServerOptions.GetDebugLog(configuration);

View File

@ -33,6 +33,7 @@ using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.Net.Http.Headers;
using NicolasDorier.RateLimits;
namespace BTCPayServer.Hosting
{
@ -207,28 +208,45 @@ namespace BTCPayServer.Hosting
IServiceProvider prov,
BTCPayServerOptions options,
IOptions<DataDirectories> dataDirectories,
ILoggerFactory loggerFactory)
ILoggerFactory loggerFactory,
IRateLimitService rateLimits)
{
Logs.Configure(loggerFactory);
Logs.Configuration.LogInformation($"Root Path: {options.RootPath}");
if (options.RootPath.Equals("/", StringComparison.OrdinalIgnoreCase))
{
ConfigureCore(app, env, prov, dataDirectories);
ConfigureCore(app, env, prov, dataDirectories, rateLimits);
}
else
{
app.Map(options.RootPath, appChild =>
{
ConfigureCore(appChild, env, prov, dataDirectories);
ConfigureCore(appChild, env, prov, dataDirectories, rateLimits);
});
}
}
private void ConfigureCore(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider prov, IOptions<DataDirectories> dataDirectories)
private void ConfigureCore(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider prov, IOptions<DataDirectories> dataDirectories, IRateLimitService rateLimits)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
rateLimits.SetZone($"zone={ZoneLimits.Login} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PublicInvoices} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Register} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PayJoin} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Shopify} rate=1000r/min burst=100 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.ForgotPassword} rate=5r/d burst=3 nodelay");
}
else
{
rateLimits.SetZone($"zone={ZoneLimits.Login} rate=5r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PublicInvoices} rate=4r/min burst=10 delay=3");
rateLimits.SetZone($"zone={ZoneLimits.Register} rate=2r/min burst=2 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.PayJoin} rate=5r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.Shopify} rate=20r/min burst=3 nodelay");
rateLimits.SetZone($"zone={ZoneLimits.ForgotPassword} rate=5r/d burst=5 nodelay");
}
app.UseHeadersOverride();
var forwardingOptions = new ForwardedHeadersOptions()
{

View File

@ -7,5 +7,6 @@ namespace BTCPayServer
public const string PayJoin = "PayJoin";
public const string Shopify = nameof(Shopify);
public const string ForgotPassword = "forgotpassword";
public const string PublicInvoices = "publicinvoices";
}
}