Replay Attack in Aspnet
How Replay Attack Manifests in ASP.NET
A replay attack in ASP.NET occurs when an attacker intercepts a valid request (e.g., a JWT token, a form post, or a Web API call) and resubmits it to achieve unauthorized actions. ASP.NET applications are particularly vulnerable when they rely solely on stateless tokens like JWTs without additional freshness guarantees, or when they misuse built-in anti-forgery mechanisms.
1. JWT Without Nonce or Timestamp Validation
Many ASP.NET Core applications use Microsoft.AspNetCore.Authentication.JwtBearer but fail to validate token issuance time or include a one-time nonce. An attacker can replay a captured JWT until it expires. Example vulnerable configuration:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = false, // VULNERABLE: No expiry check
ValidateIssuerSigningKey = true
};
});Here, ValidateLifetime = false ignores exp and nbf claims, making tokens valid indefinitely.
2. ViewState MAC Bypass in ASP.NET Web Forms
Legacy ASP.NET Web Forms uses ViewState for page state persistence. If ViewStateUserKey is not set per-session, an attacker can replay a ViewState payload from another user or session. Example vulnerable page:
public partial class VulnerablePage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// Missing: ViewStateUserKey = Session.SessionID;
if (IsPostBack)
{
var data = ViewState["SensitiveData"];
// Process data...
}
}
}Without per-session ViewStateUserKey, the MAC validation cannot bind the ViewState to a specific user session.
3. Anti-Forgery Token Reuse in MVC/Razor Pages
ASP.NET Core's anti-forgery tokens (@Html.AntiForgeryToken()) are tied to the user's identity and session. However, if an application disables double-submit cookie validation or uses static tokens, replay becomes possible. Example misconfiguration:
services.AddAntiforgery(options =>
{
options.Cookie.Name = "XSRF-TOKEN";
options.HeaderName = "X-XSRF-TOKEN";
options.SuppressXFrameOptionsHeader = false,
// VULNERABLE: Disables requirement for cookie + header match
options.RequireSsl = false; // If HTTP, token can be sniffed
});If RequireSsl = false on an HTTP site, an attacker on the same network can steal the cookie and header token and replay the request.
ASP.NET-Specific Detection
Detecting replay vulnerabilities in ASP.NET requires testing both configuration and runtime behavior. Manual testing involves capturing requests (e.g., with Burp Suite) and replaying them after logout or from a different session. However, this is time-consuming and easy to miss edge cases.
How middleBrick Scans for Replay Issues
middleBrick's ASP.NET-focused checks include:
- JWT Lifetime Validation: Sends a request without an
expclaim or with an expired token and checks if the endpoint accepts it. - ViewState MAC Binding: Analyzes ASP.NET Web Forms responses for
__VIEWSTATEfields and tests if altering the ViewState or reusing it across sessions is rejected. - Anti-Forgery Token Reuse: Extracts anti-forgery cookies and request headers, then replays them in a new session to see if validation fails.
- Nonce/Marker Presence: Checks for nonce or timestamp parameters in sensitive endpoints (e.g., financial transactions) and tests if duplicates are accepted.
Example Scan Output
After scanning an ASP.NET Core API, middleBrick returns a per-category breakdown. For replay risk, it might show:
| Check | Status | Severity | Finding |
|---|---|---|---|
| JWT Lifetime Validation | Failed | High | Token validation ignores exp claim |
| Anti-Forgery Replay | Passed | - | Tokens properly bound to session |
Using middleBrick CLI for ASP.NET Scans
Scan an ASP.NET Web API endpoint from your terminal:
middlebrick scan https://api.example.com/account/transferThe scan takes 5–15 seconds and tests unauthenticated paths first, then attempts authenticated replay if credentials are provided (optional). The JSON report includes:
{
"score": 65,
"category_breakdown": {
"replay_attack": {
"risk": "high",
"findings": [
{
"title": "JWT lifetime validation disabled",
"evidence": "Token with exp=0 accepted",
"remediation": "Set TokenValidationParameters.ValidateLifetime = true"
}
]
}
}
}You can integrate this into CI/CD via the middlebrick npm package or GitHub Action to fail builds if replay risk exceeds your threshold.
ASP.NET-Specific Remediation
Fixing replay vulnerabilities in ASP.NET involves leveraging built-in security features correctly. Below are code-level fixes for the common patterns.
1. Enforce JWT Lifetime and Nonce
Always enable lifetime validation and consider adding a one-time nonce (jti claim) with a server-side cache for critical operations.
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true, // FIX: Enable expiry validation
ValidateIssuerSigningKey = true,
ClockSkew = TimeSpan.FromMinutes(2) // Small leeway for clock drift
};
// Optional: Validate jti against a distributed cache for nonce
options.Events = new JwtBearerEvents
{
OnTokenValidated = async context =>
{
var jti = context.Principal.FindFirst("jti")?.Value;
if (string.IsNullOrEmpty(jti) || await NonceExistsAsync(jti))
{
context.Fail("Token nonce already used or missing");
}
await MarkNonceUsedAsync(jti);
}
};
});2. Bind ViewState to Session in Web Forms
Set ViewStateUserKey to a per-session value in Page_Init:
public partial class SecurePage : System.Web.UI.Page
{
protected override void OnInit(EventArgs e)
{
base.OnInit(e);
// FIX: Bind ViewState to session ID
ViewStateUserKey = Session.SessionID;
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
var data = ViewState["SensitiveData"];
// Process safely
}
}
}3. Strengthen Anti-Forgery in ASP.NET Core MVC
Ensure anti-forgery tokens require both cookie and header, and always use HTTPS.
// In Program.cs or Startup.cs
services.AddAntiforgery(options =>
{
options.Cookie.Name = "XSRF-COOKIE";
options.HeaderName = "X-XSRF-TOKEN";
options.RequireSsl = true; // FIX: Enforce HTTPS
options.SuppressXFrameOptionsHeader = false;
});
// In a Razor Page or MVC View
@{
// Generates both cookie and request token
@Html.AntiForgeryToken()
}
// In JavaScript (for AJAX):
// Read the cookie and set the header automatically
4. Add Idempotency Keys for Financial Operations
For endpoints like fund transfers, require an Idempotency-Key header and store processed keys in a database or distributed cache.
[HttpPost("transfer")]
public async Task Transfer([FromBody] TransferRequest request, [FromHeader(Name = "Idempotency-Key")] string idempotencyKey)
{
if (string.IsNullOrEmpty(idempotencyKey))
return BadRequest("Idempotency-Key required");
if (await IdempotencyKeyExistsAsync(idempotencyKey))
return Conflict("Duplicate request");
await SaveIdempotencyKeyAsync(idempotencyKey);
// Process transfer...
return Ok();
} Validation with middleBrick
After applying fixes, re-scan with middleBrick to confirm the replay risk score improves. The Pro plan's continuous monitoring can alert you if a future deployment accidentally reverts these configurations.
Frequently Asked Questions
Can I use JWT securely in ASP.NET without replay risk?
ValidateLifetime = true) and consider adding a nonce (jti claim) with server-side tracking for high-risk operations. Always use short expiration times and refresh tokens.