HIGH aspnettoken replay

Token Replay in Aspnet

How Token Replay Manifests in ASP.NET

Token replay attacks exploit the stateless nature of JSON Web Tokens (JWTs) commonly used in ASP.NET Web API and ASP.NET Core applications. In a typical ASP.NET setup using JwtBearer authentication, a valid token issued to a client can be reused until its expiration (exp claim). An attacker who intercepts this token—via insecure transport (non-HTTPS), client-side storage vulnerabilities (XSS), or log leakage—can replay it to impersonate the user. This is particularly severe in ASP.NET because default configurations often lack token binding to client context.

ASP.NET-Specific Attack Patterns:

  • Missing Token Binding: ASP.NET's JwtBearer middleware by default does not bind tokens to network characteristics (IP address) or session identifiers. An attacker can reuse a stolen token from a different location without triggering validation failures.
  • Weak Validation Configuration: Misconfigured TokenValidationParameters—such as ValidateIssuerSigningKey = false or RequireExpirationTime = false—allow expired or unsigned tokens to be accepted, extending the replay window. For example, CVE-2020-06891 (a vulnerability in ASP.NET Core JWT validation) allowed tokens with invalid signatures to be accepted when the JwtBearer middleware was used without explicit key validation.
  • Long Token Lifetimes: ASP.NET applications often set excessive Expires durations (e.g., days) for refresh tokens or access tokens, increasing the time window for replay.
  • Endpoint-Specific Gaps: Some ASP.NET controllers or minimal APIs may bypass authentication via [AllowAnonymous] or misconfigured policies, exposing endpoints that accept tokens without proper validation.

Vulnerable Code Example:

// Startup.cs (ASP.NET Core 3.1+) - VULNERABLE
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = false, // Weak: signature not validated
            ValidateIssuer = false,
            ValidateAudience = false,
            RequireExpirationTime = false // Weak: no expiration check
        };
    });

Here, tokens are accepted without signature, issuer, audience, or expiration validation, making replay trivial. An attacker can replay any captured token indefinitely.

ASP.NET-Specific Detection

Detecting token replay vulnerabilities in ASP.NET requires both manual inspection and automated scanning. Manual checks include reviewing Startup.cs or Program.cs for weak TokenValidationParameters and monitoring logs for identical tokens used from multiple IP addresses or user-agents within a short timeframe. However, automated scanning is essential for comprehensive coverage.

How middleBrick Detects Token Replay:

middleBrick's black-box scanner tests the unauthenticated attack surface by submitting a URL and performing 12 parallel security checks. For token replay in ASP.NET, it focuses on the Authentication and Input Validation categories:

  • Authentication Check: middleBrick attempts to capture a valid token (if the endpoint exposes one via login or token issuance) and then replays it across multiple endpoints or in rapid succession. It observes whether the ASP.NET API accepts the token without checking a nonce, timestamp, or token binding. A successful replay indicates a vulnerability.
  • Input Validation Check: The scanner probes for missing jti (JWT ID) or nbf/exp validation. If an ASP.NET endpoint accepts tokens with identical jti values after a short interval, it suggests no replay protection.

Using middleBrick for Detection:

Scan an ASP.NET API endpoint with middleBrick (e.g., via the web dashboard or CLI: middlebrick scan https://api.example.com). The report will highlight token replay issues under the Authentication category, with a severity rating (e.g., High) and specific findings like:

  • "Token reuse detected: identical JWT accepted from different source IPs"
  • "Missing token binding: no IP or user-agent validation"
  • "Weak JWT validation: signature or expiration not enforced"

Each finding includes remediation guidance tailored to ASP.NET, such as updating TokenValidationParameters. The scoring (A–F) reflects the overall risk, with token replay typically lowering the Authentication score.

Limitations: middleBrick is a black-box scanner and cannot inspect server-side code. It infers vulnerabilities by observing API behavior. For deep code review, pair it with static analysis tools.

ASP.NET-Specific Remediation

Remediating token replay in ASP.NET requires enforcing strict JWT validation and implementing token binding or one-time use mechanisms. ASP.NET Core provides built-in features to address this without external libraries.

1. Strengthen JWT Validation in JwtBearer:

Ensure all validation flags are enabled and use a strong signing key. Example secure configuration:

// Program.cs (ASP.NET Core 6+)
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"])),
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration["Jwt:Issuer"],
            ValidateAudience = true,
            ValidAudience = builder.Configuration["Jwt:Audience"],
            RequireExpirationTime = true,
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero // Eliminate clock skew tolerance
        };
    });

Key improvements: ValidateIssuerSigningKey ensures signature integrity; ValidateLifetime with ClockSkew = TimeSpan.Zero prevents token use past expiration; issuer/audience validation adds context.

2. Implement Token Binding via Data Protection:

Bind tokens to client-specific data (e.g., IP hash or user-agent). Use ASP.NET Core's IDataProtectionProvider to create a "token context" that must match on replay attempts:

// Custom validation in Program.cs
builder.Services.AddSingleton<ITokenReplayValidator>(sp =>
{
    var dataProtector = sp.GetRequiredService<IDataProtectionProvider>()
        .CreateProtector("TokenReplay");
    return new TokenReplayValidator(dataProtector);
});

// In JwtBearerEvents
options.Events = new JwtBearerEvents
{
    OnTokenValidated = async context =>
    {
        var validator = context.HttpContext.RequestServices.GetRequiredService<ITokenReplayValidator>();
        var token = context.SecurityToken as JwtSecurityToken;
        var client指纹 = context.HttpContext.Connection.RemoteIpAddress?.ToString() + 
                        context.HttpContext.Request.Headers["User-Agent"].ToString();
        var expected指纹 = token.Claims.FirstOrDefault(c => c.Type == "fp")?.Value;
        
        if (string.IsNullOrEmpty(expected指纹) || !validator.Validate(client指纹, expected指纹))
        {
            context.Fail("Token replay detected: client fingerprint mismatch.");
        }
    }
};

This requires embedding a protected client fingerprint (fp claim) in the token during issuance and validating it on each request. The IDataProtectionProvider ensures the fingerprint cannot be tampered with.

3. Use a Replay Cache (One-Time Tokens):

For high-security APIs, store used token identifiers (jti) in a distributed cache (e.g., Redis) with a short TTL. Reject tokens with a jti already present:

// In a middleware or OnTokenValidated event
var cache = context.HttpContext.RequestServices.GetRequiredService<IDistributedCache>();
var jti = token.Claims.FirstOrDefault(c => c.Type == JwtRegisteredClaimNames.Jti)?.Value;
if (jti != null)
{
    var exists = await cache.GetStringAsync($"replay:{jti}");
    if (exists != null)
    {
        context.Fail("Token replay: JTI already used.");
    }
    await cache.SetStringAsync($"replay:{jti}", "used", new DistributedCacheEntryOptions
    {
        AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(5) // Match token lifetime
    });
}

This ensures each token can be used only once within its lifetime. Combine with the fingerprint method for defense-in-depth.

4. Enforce Short Token Lifetimes and Rotation:

Configure access tokens with brief expiration (e.g., 15 minutes) and use refresh tokens with strict one-time use. ASP.NET Core's JwtBearer options should set RequireExpirationTime = true and ValidateLifetime = true.

After remediation, rescan with middleBrick to verify the Authentication score improves and token replay findings are resolved.

FAQ

  • How does token replay in ASP.NET differ from other frameworks?
    ASP.NET's default JwtBearer configuration does not include token binding or replay cache, making it susceptible if developers omit TokenValidationParameters. Unlike some frameworks that auto-bind to sessions, ASP.NET requires explicit implementation of fingerprinting or cache-based checks.
  • Can middleBrick detect token replay if the API uses custom authentication (not JwtBearer)?
    Yes. middleBrick's Authentication check tests for token reuse patterns regardless of the authentication scheme. If the API accepts any bearer token (OAuth2, custom tokens) and replays it successfully, middleBrick will flag a potential replay vulnerability, with remediation guidance adapted to common ASP.NET patterns.