Review explicitly disabled request validation
Matches ASP.NET MVC controllers, actions and model properties that explicitly opt out of request validation with [ValidateInput(false)] or [AllowHtml].
Request validation is the ASP.NET safety net that rejects requests containing HTML-looking input. Disabling it is legitimate for rich-text editors, but then every value received through that action or property can carry a script: it must be sanitized with an allow-list sanitizer before being stored, and HTML-encoded whenever it is rendered. A controller-level [ValidateInput(false)] is reported with the High severity since it opens every action of the controller.
This is CWE-79 Cross-site Scripting (OWASP Top 10 A03 Injection). ASP.NET Core has no request validation: the rule is silent there.
Flagged Code
Request validation is the ASP.NET safety net that rejects requests containing HTML-looking input. Disabling it is legitimate for rich-text editors, but then every value received through that action or property can carry a script: it must be sanitized with an allow-list sanitizer before being stored, and HTML-encoded whenever it is rendered. A controller-level [ValidateInput(false)] is reported with the High severity since it opens every action of the controller.
This is CWE-79 Cross-site Scripting (OWASP Top 10 A03 Injection). ASP.NET Core has no request validation: the rule is silent there.
Flagged Code
[ValidateInput(false)] // flagged: whole controller
public class ArticleController : Controller {
[HttpPost]
public ActionResult Save(Article article) {
_repository.Save(article); // raw HTML stored as-is
return RedirectToAction("Index");
}
}
Fixed Codepublic class ArticleController : Controller { // request validation stays on
[HttpPost]
public ActionResult Save(Article article) { // article.Body is Markdown, not HTML
_repository.Save(article); // markup submissions are rejected
return RedirectToAction("Index");
}
}How to fix
Keep request validation on and accept a format that carries no markup (Markdown, BBCode, plain text) rendered server-side. When rich HTML must be accepted, narrow the opt-out to the single property that receives it ([AllowHtml] on that property, never [ValidateInput(false)] on a controller), sanitize the value with an allow-list HTML sanitizer before storing it, rely on Razor encoding when rendering it, then suppress the issue on that property.
CQLinq source
This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.
// <Name>Review explicitly disabled request validation</Name>
// <Id>ND3134:ReviewExplicitlyDisabledRequestValidation</Id>
warnif count > 0
// Third-party types are part of the code model only when the application uses them.
// ASP.NET MVC 5 / Web Forms request validation rejects HTML-looking input; these two attributes disable it.
// ASP.NET Core has no request validation, so this rule is silent on ASP.NET Core applications.
let validateInputAttr = ThirdParty.Types.WithFullName("System.Web.Mvc.ValidateInputAttribute").FirstOrDefault()
let allowHtmlAttr = ThirdParty.Types.WithFullName("System.Web.Mvc.AllowHtmlAttribute").FirstOrDefault()
where validateInputAttr != null || allowHtmlAttr != null
// [ValidateInput(false)]: the boolean is the first constructor argument, readable from the attribute metadata.
let disablesValidation = new Func<IAttributeTarget, bool>(x =>
(validateInputAttr != null &&
x.AttributeTagsOf(validateInputAttr).Any(tag =>
tag.Parameters.Any(p => p.Name == "Arg0" && p.Value.ToString().ToLower() == "false"))) ||
(allowHtmlAttr != null && x.AttributeClassesThatTagMe.Contains(allowHtmlAttr)))
from t in Application.Types
where !t.IsGeneratedByCompiler
// [ValidateInput(false)] on an action or a controller, [AllowHtml] on a model property (its getter and setter)
let membersDisablingValidation = t.Methods.Cast<IMember>().Concat(t.Fields.Cast<IMember>())
.Where(x => disablesValidation(x)).ToArray()
let typeDisablesValidation = disablesValidation(t)
where typeDisablesValidation || membersDisablingValidation.Length > 0
select new {
t,
membersDisablingValidation,
typeDisablesValidation,
Debt = (10 + 10 * membersDisablingValidation.Length).ToMinutes().ToDebt(),
Severity = typeDisablesValidation ? Severity.High : Severity.Medium
}
//<Expl>
// The type *{0}* disables ASP.NET request validation, on itself or on
// *{1}*. Markup and script submitted by a caller then reach the
// application unfiltered: make sure it is encoded or sanitized before
// being stored or rendered.
//</Expl>
// <Desc>
// Matches ASP.NET MVC controllers, actions and model properties that
// explicitly opt out of request validation with *[ValidateInput(false)]*
// or *[AllowHtml]*.
//
// Request validation is the ASP.NET safety net that rejects requests
// containing HTML-looking input. Disabling it is legitimate for rich-text
// editors, but then every value received through that action or property
// can carry a script: it must be sanitized with an allow-list sanitizer
// before being stored, and HTML-encoded whenever it is rendered. A
// controller-level *[ValidateInput(false)]* is reported with the *High*
// severity since it opens every action of the controller.
//
// This is CWE-79 *Cross-site Scripting* (OWASP Top 10 A03 Injection).
// ASP.NET Core has no request validation: the rule is silent there.
//
// **Flagged Code**
// ```csharp
// [ValidateInput(false)] // flagged: whole controller
// public class ArticleController : Controller {
// [HttpPost]
// public ActionResult Save(Article article) {
// _repository.Save(article); // raw HTML stored as-is
// return RedirectToAction("Index");
// }
// }
// ```
//
// **Fixed Code**
// ```csharp
// public class ArticleController : Controller { // request validation stays on
// [HttpPost]
// public ActionResult Save(Article article) { // article.Body is Markdown, not HTML
// _repository.Save(article); // markup submissions are rejected
// return RedirectToAction("Index");
// }
// }
// ```
// </Desc>
// <HowToFix>
// Keep request validation on and accept a format that carries no markup
// (Markdown, BBCode, plain text) rendered server-side. When rich HTML
// must be accepted, narrow the opt-out to the single property that
// receives it (*[AllowHtml]* on that property, never
// *[ValidateInput(false)]* on a controller), sanitize the value with an
// allow-list HTML sanitizer before storing it, rely on Razor encoding
// when rendering it, then suppress the issue on that property.
// </HowToFix>
Open in the interactive Rules Explorer · All default rules
Security: all 39 items
- Software Composition Analysis (SCA) ND3120
- Transitive Software Composition Analysis (SCA) ND3121
- Don't hard-code secrets in constants ND3114
- Review credentials built from hard-coded values ND3115
- Don't use insecure deserializers ND3110
- Don't use weak hash algorithms (MD5, SHA1) ND3109
- Don't use DES/3DES/RC2 weak cipher algorithms ND3102
- Don't use weak password-based key derivation ND3123
- Don't use the ECB encryption mode ND3132
- Don't disable certificate validation ND3103
- Don't hard-code TLS/SSL protocol versions ND3122
- Review commands vulnerable to SQL injection ND3107
- Review data adapters vulnerable to SQL injection ND3108
- Review raw SQL built from composed strings ND3128
- Review LDAP filters composed from strings ND3137
- Don't enable external XML entity resolution (XXE) ND3111
- Review processes started with a composed arguments string ND3124
- Review web endpoints building file paths from their parameters ND3129
- Review custom archive extraction (Zip Slip) ND3135
- Review assemblies and types loaded from runtime-provided names ND3125
- Review web endpoints without [Authorize] nor [AllowAnonymous] ND3118
- Review state-changing endpoints opened to anonymous callers ND3130
- Review state-changing MVC actions without anti-forgery validation ND3119
- Review HTML-encoding bypasses ND3133
- Review explicitly disabled request validation ND3134
- Review redirects to a URL that is not validated as local ND3126
- Review request-influenced outbound HTTP calls (SSRF) ND3136
- Review insecure ASP.NET Core CORS policies and unguarded developer exception page ND3116
- Review cookies created without HttpOnly and Secure flags ND3117
- Review regular expressions without a match timeout (ReDoS) ND3112
- Review secrets compared with non-constant-time equality ND3113
- Review secrets passed to logging methods ND3127
- Review security operations that log nothing ND3131
- Don't use System.Random for security purposes ND3101
- Don't use CoSetProxyBlanket and CoInitializeSecurity ND3100
- Review publicly visible event handlers ND3104
- Pointers should not be publicly visible ND3105
- Seal methods that satisfy non-public interfaces ND3106
- Restrict sensitive APIs to reviewed components ND3138