Xss Cross Site Scripting in Aspnet with Hmac Signatures
Xss Cross Site Scripting in Aspnet with Hmac Signatures
Cross-site scripting (XSS) in ASP.NET can occur when untrusted data is rendered in the browser without proper validation or encoding. HMAC signatures are commonly used to verify integrity and authenticity of data such as anti-forgery tokens, query parameters, or form payloads. When HMAC verification is implemented inconsistently or applied only to a subset of inputs, it can create a false sense of security and expose XSS risks.
Consider an endpoint that accepts an HMAC-signed query parameter id and a signature signature. If the server validates the HMAC but then directly injects the untrusted id value into the response without HTML encoding, an attacker can craft a signed payload that executes script in the victim’s browser. This happens because the signature ensures the parameter was not tampered with, but does not guarantee the parameter is safe for HTML context. For example, an unsigned or malformed input path may be handled differently, allowing an attacker to bypass checks by using an unsigned vector or by manipulating parameter ordering.
In ASP.NET, XSS with HMAC signatures can also emerge when the signature covers only a portion of the request. Developers might sign the payload but forget to include dangerous parameters in the signed scope, or they may serialize objects to JSON or form data and sign the serialized string, then later deserialize and render properties without encoding. If an attacker can control parts of the JSON or form data outside the signed scope, they may inject script-bearing values that the server trusts because other signed fields appear valid.
Another scenario involves reflection-based model binding where an attacker supplies unexpected parameter names. Even if the HMAC validates, the application may bind and render user-controlled properties that contain unencoded HTML. ASP.NET’s default model binder can map incoming data to complex objects; if the developer does not validate or encode each rendered property, stored XSS can occur. Additionally, if logs or error messages include raw HMAC-signed values and those values are rendered in an admin UI without encoding, the attack surface extends to internal tools.
Real-world patterns include query strings like /search?id=123&signature=abc... where id is rendered in a results page, or POST bodies where a JSON field is signed but later displayed in JavaScript contexts. Attackers probe for endpoints with HMAC protections and test whether reflected values are HTML-encoded. They may also test fallback paths where HMAC validation is skipped, attempting to deliver script through unsigned routes.
Hmac Signatures-Specific Remediation in Aspnet
Remediation focuses on never trusting data solely because it has a valid HMAC. Always treat input as untrusted and apply context-aware encoding before rendering. Below are concrete practices and code examples for ASP.NET.
- Validate HMAC and then explicitly encode output based on context. For HTML body content, use HTML encoding; for JavaScript, use JavaScript string encoding; for URLs, use URL encoding. Never concatenate user data into HTML or script without encoding.
- Ensure the signed scope includes all parameters that influence rendering. If you sign a subset, attackers can manipulate unsigned parameters to inject unsafe values.
- Use strongly typed models with validation attributes and avoid reflection-based binding of untrusted data to sensitive properties.
Example: HMAC validation with safe HTML rendering in ASP.NET Core.
using System.Security.Cryptography;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
public static class HmacHelper
{
private static readonly byte[] Key = Encoding.UTF8.GetBytes("REPLACE_WITH_STRONG_KEY_BASE64_OR_CONFIG");
public static bool VerifyQueryParam(string value, string receivedSignature)
{
using var hmac = new HMACSHA256(Key);
var computed = hmac.ComputeHash(Encoding.UTF8.GetBytes(value));
var computedSignature = WebEncoders.Base64UrlEncode(computed);
return CryptographicOperations.FixedTimeEquals(
Convert.FromBase64String(receivedSignature),
Convert.FromBase64String(computedSignature));
}
}
[ApiController]
[Route("[controller]")]
public class SearchController : ControllerBase
{
[HttpGet]
public IActionResult Get([FromQuery] string id, [FromQuery] string signature)
{
if (string.IsNullOrEmpty(id) || string.IsNullOrEmpty(signature))
{
return BadRequest("Missing id or signature.");
}
if (!HmacHelper.VerifyQueryParam(id, signature))
{
return Unauthorized("Invalid signature.");
}
// Treat 'id' as untrusted data; encode for HTML context
var safeId = System.Net.WebUtility.HtmlEncode(id);
var model = $"Search results for: {safeId}";
return Content(model, "text/html");
}
}
Example: HMAC validation for JSON payload with encoding before JavaScript rendering (e.g., in a Razor view or Blazor component).
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.WebUtilities;
public static class JsonHmacValidator
{
private static readonly byte[] Key = Encoding.UTF8.GetBytes("REPLACE_WITH_STRONG_KEY_BASE64_OR_CONFIG");
public static bool VerifyJsonHmac(string jsonPayload, string receivedSignature)
{
using var hmac = new HMACSHA256(Key);
var data = Encoding.UTF8.GetBytes(jsonPayload);
var hash = hmac.ComputeHash(data);
var computedSignature = WebEncoders.Base64UrlEncode(hash);
return CryptographicOperations.FixedTimeEquals(
Convert.FromBase64String(receivedSignature),
Convert.FromBase64String(computedSignature));
}
}
[ApiController]
[Route("[controller]")]
public class CommentController : ControllerBase
{
[HttpPost]
public IActionResult Post([FromBody] JsonElement payload)
{
// Assume the client sends the JSON string and a signature header
var jsonString = payload.GetRawText();
var signature = Request.Headers["X-Payload-Signature"].ToString();
if (!JsonHmacValidator.VerifyJsonHmac(jsonString, signature))
{
return Unauthorized("Invalid signature.");
n }
using var doc = JsonDocument.Parse(jsonString);
var comment = doc.RootElement.GetProperty("comment").GetString() ?? string.Empty;
// Encode for HTML context before rendering
var safeComment = System.Net.WebUtility.HtmlEncode(comment);
var model = $"User comment: {safeComment}";
return Content(model, "text/html");
}
}
Additional guidance:
- Use
System.Net.WebUtility.HtmlEncodeor tag builders to encode dynamic strings inserted into HTML. - For JavaScript contexts, use appropriate escaping (e.g., JSON serialization for inline script) and avoid inline event handlers.
- Ensure HMAC keys are stored securely (e.g., using configuration providers and environment variables) and rotated periodically.
- Apply the same encoding discipline in partial views, tag helpers, and when populating JavaScript variables from server data.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |