Mastodon

Do you allow XSS in your passwords? You should!

There are two security principles which I hold dearly but are often counterintuitive:

  1. Users should be able to create any conceivable password they desire – no limits!
  2. All input should be treated as hostile and properly sanitised against a whitelist.

This is counterintuitive advice in so far as that second point has always been partially supported natively by ASP.NET request validation. I say “partially” because it’s not the final word in request validation and you should always properly whitelist your allowable input in addition to the native framework defences. That said, you should always (at least wherever possible) leave request validation enabled and in the past I’ve been critical of those who don’t. It’s important enough that I rolled a test for this into ASafaWeb.

Getting back to these principles being counterintuitive, last month I got an interesting message from a friendly ASafaWeb supporter:

@troyhunt issue with asafaweb registration mate - used 1password to generate 20char and I got a 'bugger' error - managed a less secure pw ok

Bugger. Of course what was happening is actually quite clear, you just need to try changing your password to “<Script>”:

Request validation firing on the password field

Clearly this is my development environment given the internal error message, but this is precisely what was happening on the public site too – request validation was rejecting the input.

So why is this necessary for a password field? Well firstly, ASP.NET doesn’t discriminate; post data is post data and if it contains potentially malicious script then it’s rejected. But why should you worry about a potential XSS attack in a password field? I mean it’s not like you’re ever going to render it to a page or email, right (cough, Tesco)? Of course the first thing that should happen to a password when it hits the web server is that it should be cryptographically hashed and never rendered to any output context again. Ever.

In the old days, the only way to enable XSS in your passwords would have been to disable request validation on the page. Unfortunately this would also mean disabling it on all the other fields thus allowing XSS in the name, address, phone number and wherever else the developer wasn’t explicitly whitelisting trusted data (which, unfortunately, is usually everywhere). In reality, request validation was often disabled across the entire site which is definitely undesirable.

Enter .NET 4.5 and two very cool new additions to request validation:

  1. Deferred or “lazy” validation: only the data which is actually accessed is subject to validation. Post (or get) a randomly named piece of data and it won’t get validated.
  2. Unvalidated requests: not all data needs to be subject to request validation and now it can be selectively ignored in a few different ways.

It’s that second point which means that XSS in password fields is now a reality without compromising the security of the fields which you still want validated. There are various means of implementing this within ASP.NET MVC and not validating data.

For example, the controller action can be directed to not validate the input:

[HttpPost]
[ValidateInput(false)]
public ActionResult ChangePassword(ChangePasswordModel model)

That works for a change password feature where you normally just have three passwords in the post data (old, new, confirm new), but how about at registration? You don’t want to disable request validation on all those other fields.

Fortunately we can just drop down to the model level and disable validation on an attribute by using the AllowHtml data annotation:

[AllowHtml]
[Required(ErrorMessage = "Password is required")]
[StringLength(100, ErrorMessage = "The password must be at least {2} 
characters long"
, MinimumLength = 8)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; }

There are a few other options available on the request object as well (particularly useful for web forms), but it really can’t get much easier than this in the MVC world – just add the data annotation to each password attribute.

This is only possible with .NET 4.5 and it’s one of the many features I can now leverage in ASafaWeb after recently upgrading the framework and having AppHarbor support it by being earlier adopters of the new version in their hosting environment. So for users of ASafaWeb, go nuts with XSS in your passwords! As for those of you with pre-ASP.NET 4.5 sites, why are you still restricting my password options?! :)

Edit (9 Sep): I need to clarify this a little better (good reminder of why I try not to rush blog posts!) – the ValidateInput and AllowHtml attributes used on controller actions and class properties has been available in MVC for a little while (pre .NET 4.5) and you’ll find them in the documentation for ASP.NET 4 request validation. What’s entirely new for 4.5 is the ability to selectively disable it on individual request properties. The documentation for ASP.NET 4.5 request validation gives a good summary:

Request.Form["userInput"]; // Validated, error if input includes markup

Request.Unvalidated("userInput"); // Validation bypassed
Request.Unvalidated().Form["userInput"]; // Validation bypassed

Request.QueryString["userPreference"]; // Validated
Request.Unvalidated().QueryString["userPreference"]; // Validation bypassed
Security .NET ASafaWeb
Tweet Post Update Email RSS

Hi, I'm Troy Hunt, I write this blog, create courses for Pluralsight and am a Microsoft Regional Director and MVP who travels the world speaking at events and training technology professionals