Skip to content

Commit 25660cb

Browse files
authored
Add CSRF benchmark (#2181)
1 parent e5d4b80 commit 25660cb

4 files changed

Lines changed: 118 additions & 21 deletions

File tree

build/trend-scenarios.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ parameters:
9999
arguments: --scenario antiforgery-validation $(antiforgeryJobs) --property scenario=AntiforgeryTokenValidation
100100
- displayName: Antiforgery Token Generation
101101
arguments: --scenario antiforgery-generation $(antiforgeryJobs) --property scenario=AntiforgeryTokenGeneration
102+
- displayName: CSRF Accepted
103+
arguments: --scenario csrf-accepted $(antiforgeryJobs) --property scenario=AntiforgeryCsrfAccepted
104+
- displayName: CSRF Rejected
105+
arguments: --scenario csrf-rejected $(antiforgeryJobs) --property scenario=AntiforgeryCsrfRejected
102106

103107
# TLS
104108

scenarios/antiforgery.benchmarks.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ imports:
55
variables:
66
serverPort: 5000
77
serverScheme: http
8+
scenario: antiforgery
9+
debug: false
810

911
jobs:
1012
antiforgerybenchmarks:
@@ -13,7 +15,7 @@ jobs:
1315
branchOrCommit: main
1416
project: src/BenchmarksApps/Antiforgery/Antiforgery.csproj
1517
readyStateText: Application started.
16-
arguments: "--urls {{serverScheme}}://{{serverAddress}}:{{serverPort}}"
18+
arguments: "--urls {{serverScheme}}://{{serverAddress}}:{{serverPort}} --scenario {{scenario}} --debug {{debug}}"
1719

1820
scenarios:
1921

@@ -45,4 +47,30 @@ scenarios:
4547
load:
4648
job: wrk
4749
variables:
48-
path: /noOp
50+
path: /noOp
51+
52+
csrf-accepted:
53+
application:
54+
job: antiforgerybenchmarks
55+
variables:
56+
scenario: csrf
57+
load:
58+
job: wrk
59+
variables:
60+
path: /csrf
61+
script: https://raw.githubusercontent.com/aspnet/Benchmarks/main/src/BenchmarksApps/Antiforgery/scripts/wrk-csrf.lua
62+
customHeaders:
63+
- "Sec-Fetch-Site: same-origin"
64+
65+
csrf-rejected:
66+
application:
67+
job: antiforgerybenchmarks
68+
variables:
69+
scenario: csrf
70+
load:
71+
job: wrk
72+
variables:
73+
path: /csrf
74+
script: https://raw.githubusercontent.com/aspnet/Benchmarks/main/src/BenchmarksApps/Antiforgery/scripts/wrk-csrf.lua
75+
customHeaders:
76+
- "Sec-Fetch-Site: cross-site"

src/BenchmarksApps/Antiforgery/Program.cs

Lines changed: 79 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,93 @@
11
using Microsoft.AspNetCore.Antiforgery;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.Extensions.Logging;
24

35
var builder = WebApplication.CreateBuilder(args);
4-
builder.Logging.ClearProviders();
5-
builder.Services.AddAntiforgery(options => options.HeaderName = "XSRF-TOKEN");
6+
7+
var debug = builder.Configuration.GetValue<bool>("debug");
8+
if (debug)
9+
{
10+
builder.Logging.AddFilter("Microsoft", LogLevel.Warning);
11+
builder.Logging.AddFilter("Microsoft.AspNetCore.Antiforgery.CsrfProtectionMiddleware", LogLevel.Debug);
12+
}
13+
else
14+
{
15+
builder.Logging.ClearProviders();
16+
}
17+
18+
// "csrf" exercises the auto-injected cross-origin (Sec-Fetch) CSRF protection in isolation,
19+
// so the token-based antiforgery services/middleware are left out to avoid overriding its verdict.
20+
var scenario = builder.Configuration["scenario"] ?? "antiforgery";
21+
var tokenAntiforgeryEnabled = !string.Equals(scenario, "csrf", StringComparison.OrdinalIgnoreCase);
22+
Console.WriteLine($"Scenario: '{scenario}'. Token-based antiforgery enabled: {tokenAntiforgeryEnabled}.");
23+
24+
if (tokenAntiforgeryEnabled)
25+
{
26+
builder.Services.AddAntiforgery(options => options.HeaderName = "XSRF-TOKEN");
27+
}
628

729
var app = builder.Build();
8-
app.UseAntiforgery();
930

10-
app.MapGet("/", () => Results.Ok("hello world!"));
11-
app.MapGet("/noOp", (HttpContext ctx, IAntiforgery antiforgery) => Results.Ok());
31+
if (debug)
32+
{
33+
var requestCount = 0;
34+
app.Use(async (context, next) =>
35+
{
36+
var n = Interlocked.Increment(ref requestCount);
37+
if (n <= 20)
38+
{
39+
var request = context.Request;
40+
Console.WriteLine($"[debug] #{n} {request.Method} {request.Path}{request.QueryString}");
41+
foreach (var header in request.Headers)
42+
{
43+
Console.WriteLine($"[debug] #{n} {header.Key}: {header.Value}");
44+
}
45+
}
1246

13-
// GET https://localhost:55471/auth
14-
app.MapGet("/auth", (HttpContext ctx, IAntiforgery antiforgery) =>
47+
await next(context);
48+
49+
if (n <= 20)
50+
{
51+
Console.WriteLine($"[debug] #{n} -> {context.Response.StatusCode}");
52+
}
53+
});
54+
}
55+
56+
if (tokenAntiforgeryEnabled)
1557
{
16-
var token = antiforgery.GetAndStoreTokens(ctx);
17-
ctx.Response.Headers.Append("XSRF-TOKEN", token.RequestToken!);
18-
return Results.Ok();
19-
});
58+
app.UseAntiforgery();
59+
}
60+
61+
app.MapGet("/", () => Results.Ok("hello world!"));
2062

21-
// POST https://localhost:55471/validateToken
22-
app.MapPost("/validateToken", async (HttpContext ctx, IAntiforgery antiforgery) =>
63+
// POST endpoint guarded only by the auto-injected cross-origin CSRF protection.
64+
app.MapPost("/csrf", ([FromForm] string name) => Results.Ok());
65+
66+
// Token-based antiforgery endpoints. These depend on IAntiforgery, which is only
67+
// registered when the token-based antiforgery services are added above.
68+
if (tokenAntiforgeryEnabled)
2369
{
24-
// HttpContext is expected to have 2 headers:
25-
// 1) antiforgery token ("XSRF-TOKEN");
26-
// 2) cookie token ("Cookie") with value of `.AspNetCore.Antiforgery.<unique-sequence>=<cookie_header>`
70+
app.MapGet("/noOp", (HttpContext ctx, IAntiforgery antiforgery) => Results.Ok());
71+
72+
// GET https://localhost:55471/auth
73+
app.MapGet("/auth", (HttpContext ctx, IAntiforgery antiforgery) =>
74+
{
75+
var token = antiforgery.GetAndStoreTokens(ctx);
76+
ctx.Response.Headers.Append("XSRF-TOKEN", token.RequestToken!);
77+
return Results.Ok();
78+
});
79+
80+
// POST https://localhost:55471/validateToken
81+
app.MapPost("/validateToken", async (HttpContext ctx, IAntiforgery antiforgery) =>
82+
{
83+
// HttpContext is expected to have 2 headers:
84+
// 1) antiforgery token ("XSRF-TOKEN");
85+
// 2) cookie token ("Cookie") with value of `.AspNetCore.Antiforgery.<unique-sequence>=<cookie_header>`
2786

28-
await antiforgery.ValidateRequestAsync(ctx);
29-
return Results.Ok();
30-
});
87+
await antiforgery.ValidateRequestAsync(ctx);
88+
return Results.Ok();
89+
});
90+
}
3191

3292
await app.StartAsync();
3393
Console.WriteLine("Application started.");
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- POSTs a form to /csrf to exercise the auto-injected cross-origin (Sec-Fetch) CSRF protection.
2+
3+
wrk.method = "POST"
4+
wrk.headers["Content-Type"] = "application/x-www-form-urlencoded"
5+
wrk.body = "name=benchmark"

0 commit comments

Comments
 (0)