Posting disabled checkboxes from MVC Razor views

In ASP.Net MVC a common technique when dropdowns and textboxes are required to be disabled but their values still need posting back is to include a hidden backing field corresponding to the same model property. In the case of CheckBoxFor the idea is the same, except there is one extra peculiarity with CheckBoxFor due to the fact that for this HMTL  helper MVC renders a hidden text field of its own (along with the checkbox) which does not have the disabled attribute even if you have added it to the main CheckBoxFor HTML Helper. For example here is what is rendered for a disabled checkbox whose underlying bool property is set to true.

hiddentextfield

As nothing gets sent to the server if the checkbox is not checked, the binder has nothing to bind to so MVC always renders this hidden field and always sets its value to false. Due to the fact it appears after the checkbox, the true from the checkbox will bind first if the user has checked it, as MVC always binds to the first matched property. Therefore when we are dealing with disabled but checked checkboxes and we want to post the value, we need to ensure our hidden field appears before the @Html.CheckBoxFor hidden field. The first image below for example always bind as false (even though the checkbox is checked), while the second one binds are true.

after-checkbox

before-checkbox

I think this is a common enough problem, so hopefully the above helps.

Related Links

Credit to Darin Dimitrov for his answer on stackoverflow which outlines the approach above, however crucially he does not mention the importance of having the backing field before the @Html.CheckBoxFor statement which is why I imagine some people mentioned in the comments that a hidden backing field did not work for them.

MVC HTML helpers read from modelState before looking in the model

If you have posted back a form to an MVC action method and want to update some of the bound viewModel properties in that action method before redisplaying the same view, you might run into a problem whereby your HTML helpers are displaying the values as posted at binding time, but not as they were after manipulation.

Just before calling the view statement or indeed in the razor markup itself you can set a breakpoint and verify your viewModel/model have the updated values but still the old values render. This is because MVC assumes that if you’re rendering a view in response to an HTTP POST then you’re likely to be redisplaying a form that has failed validation. Since some data entered by the user and posted back may not be compatible with the model (i.e. posting a decimal number back when the corresponding model property is int) and thus MVC can’t store them in the model it stores all binded values into ModelState and looks in that before the model when rendering a view using HTML helpers such as TextBoxFor.

Example of HTML helper using ModelState not the model to render its value

For example below we can see what was posted (1) and the value of the viewModel/Model when sending it to the view (2). As we are using Html.TextBoxFor however, the rendered data is still showing ‘John’ and ‘Smith’.

viewmodel-after-binding
viewmodel-after-manual-changes

rendered-page-with-binded-values

Clearing ModelState so the model values will be used

The fix for this is to clear either ModelState completely as in 4a or just clear the ModelState properties which are relevant as in 4b images below. After clearing ModelState we can see that the manually updated properties are now displayed.

clearing specific modelstate

rendered-page-with-manual-values

I’m not sure how known this behaviour is or how many problems it causes developers but it’s one to watch out for anyhow.

Related Links

Rick Strahl’s ‘ASP.NET MVC Postbacks and HtmlHelper Controls ignoring Model Changes‘ blog post describes this behaviour and also has a section on the rationale of having MVC work this way.

Simon Ince’s article ‘ASP.NET MVC’s Html Helpers Render the Wrong Value!’ offers a number of alternative approaches that you could use rather than clearing ModelState such as not using html helpers and implementing the Post-Redirect-Get pattern.

MVC Dropdownlistfor defaulting to previously selected value when using a single SelectListItem List

Watch out for this one. If you have created a single list of SelectListItems and use this viewModel property to populate multiple DropDownListFor HTML helper controls, you will likely notice some strange behaviour.

What happens is that if one DropDownListFor has a selected value (ie. its bound property isn’t null) all subsequent DropDownListFor which are populated using the same SelectListItem List and don’t have a selected value have their default selection changed to whatever the previously selected value was. This happens even though expected behaviour would be to just leave the subsequent DropDownListFor selected to their default values.

As an example, this problem has the potential to occur below. If BirthCountry was selected to say Canada, and ResidenceCountry had no value, the 2nd dropdown would be defaulted to Canada and not “” as explicitly specified.

public IEnumerable<SelectListItem> Countries {get;set;}
@Html.DropDownListFor(x=>x.BirthCountry,Model.Countries,"") @Html.DropDownListFor(x=>x.ResidenceCountry,Model.Countries,"")

This is because SelectListItem is a reference type so when you change the Selected property to true, all dropdowns which use that SelectListItem will reflect this.

Always use distinct SelectListItem lists for each DropDownListFor

You can do this by creating them in the viewModel like:

public IEnumerable<SelectListItem> BirthCountries {get;set;}
public IEnumerable<SelectListItem> ResidenceCountries {get;set;}
@Html.DropDownListFor(x=>x.BirthCountry,Model.BirthCountries,"") @Html.DropDownListFor(x=>x.ResidenceCountry,Model.ResidenceCountries,"")

or just have a single viewModel property which is a collection of countries and then create multiple SelectListItems in your razor view:

public IEnumerable<Country> Countries { get;set}
@Html.DropDownListFor(x=>x.BirthCountry, 
    new SelectList(Model.Countries,"countryCode","countryName", Model.BirthCountry))
@Html.DropDownListFor(x=>x.ResidenceCountry, 
    new SelectList(Model.Countries,"countryCode","countryName", Model.ResidenceCountry))

Related links

Stackoverflow question with a good answer detailing when the behaviour described above occurs

Thread on forums.asp.net discussing whether this behaviour is a bug or not

Prevent account enumeration on login, reset password and registration pages

Account enumeration is a potential security risk whereby a web site gives out information about what accounts are already in the system. This may a) leave them susceptible to a brute force-esque attack and b) may violate their users privacy which may be very important for certain types of sites. There are a couple of prominent Irish websites covered below which I’m familiar with and I just happened to notice they allow account enumeration but it is important to note that the vast majority of ecommerce sites online do this too. Certainly there are greater security risks out there such as SQL Injection, badly stored passwords, not using https when you should etc. but when this exploit is found alongside other exploits… well let’s just say that’s when bad things can happen.

keep login failed messages generic

I think most developers have the possibility of account enumeration in their minds when considering the messages returned from a login page for failed logins. When logging into most websites with a non existing username you will commonly see ‘Invalid username/password combination‘ type messages rather than ‘That username does not exist‘ type messages. An attacker now cannot just keep trying usernames on the login page until they find one that does exist, at which point they may (if possible) launch a dictionary password attack against that specific account. A few examples of Irish sites doing a good job here include easons.com and water.ie:

Generic Login messages

waterie-login

Another prominent Irish site; ticketmaster.ie does present different messages to the user if the account exists or not on their login page. The following image shows the login for non existing accounts:

ticketmaster-non-existing-account

It all looks good right? No mention that the specific email address does not exist, just that the address and password as a whole did not match any accounts. If however, you know of one account that does exist (the one the hacker just created for himself) and try to login with that you’ll notice that the message is different:

ticketmaster-existing-account

Ticketmaster do appear however to have an account lock out facility which locks accounts after 5 failed accounts. What this means is that although an attacker can find out if an account exists due to the slightly different error messages returned (which would be really bad for a very private site.. think ashleymadison.com) they will only get five password tries thus a full on brute force attack against a specific account with a password dictionary is not feasible.

Although locking accounts help mitigate against brute force attacks there are a number of potential problems with this approach. It can for example leave ticketmaster.ie open to an account lockout attack, whereby the attacker can deliberately lock out a large number of accounts in a denial of service-esque attack (this is possible as ticketmaster sends a reset password rather than URL to reset a password). It also means an attacker could build a large list of valid accounts and try the same password on all of them (ticketmaster1234 perhaps) which could return a few matches. Ideally ticketmaster would not reveal if an account existed or not.

preventing account enumeration on the forgot password page?

The same idea should of course apply to the forgot/reset password pages. Unfortunately what is very common is that although a website’s login fail message is generic, the websites goes and undoes all this good work on their forgot/reset password page. Remember easons.com who displayed ‘If you have an account but can’t remember your password, click on “Password Problems?” below‘ regardless if the account existed or not? Well here’s what happens on their reset password page:

easons-forgotpassword1 easons-forgotpassword2

Remember water.ie who upon failed login just presented ‘Please enter a valid username and password’ to the user? The first image below is what the forgot your password screen shows when the account doesn’t exist, while on the right, we see what the screen shows for an account that does exist.

water-forgot-nonexistingwater-forgot-existing

 

 

 

 

 

 

So previously undiscoverable accounts are now discoverable on both easons.com and water.ie via their reset password pages. This is probably not a huge deal from a privacy perspective (does your neighbour like books? / has your neighbour paid his water charges?) for these particular sites. If however these sites don’t implement some kind of brute force mitigation individual accounts could be vulnerable. Ticketmaster.ie, as noted does appear to have brute force protection in place, this is just as well because their password reset page gives up existing accounts too:

ticketmaster-forgot
What should happen in reset password scenarios is that the website should display the same generic message regardless if the username/email address exists. A message such as ‘Further instructions have been sent to your email address‘ or similar is often used in both cases. If the email address does exist the email can contain a password reset link (not a reset password in email – search for ‘Sending a reset password versus sending a reset URL’) which the email account holder can click on if it was them that asked for the reset or ignore if it was not. If the email account doesn’t exist in the user DB, just don’t send any email.

…but what about the registration page? I have to tell a potential new user that their chosen username/email is already taken on screen!

Not necessarily. Preventing account enumeration is most problematic on the registration page that’s for sure, at least from a usability perspective. The goal here is the same as with the login and password reset pages and that is to keep the outward (what’s on the screen) process the same for usernames/emails that already exist and those that don’t.

If the site uses the email as the username, it should be possible to do this pretty nicely and in a manner similar to before. It might require some re-jigging of your registration pages, but the idea would be to start the process by requiring the user to enter their email address. When they submit this notify them that they have been sent an email with further instructions regardless if the email is in the DB or not. If the email address is already in the system the email will advise them that they have already registered and perhaps provide a link to the password reset page. If the email address is unused the email will contain a time limited URL link to verify email and continue the registration process. There is a very small usability hit on this approach as the notification about email addresses already existing now happens via email rather than on screen, but this will affect only a tiny percentage of registrations.

Both easons.com and ticketmaster.ie use the users email as their username so could use the above approach. Their current registration processes let us all know if accounts already exist or not:

easons-createaccount ticketmaster-createaccont

Note – water.ie is not really applicable in this case as their registration process begins with the entering of a application ID and associated pin which has been sent out in the post.

What if a website uses arbitrarily chosen usernames rather than email addresses to login? Well, for sure preventing account enumeration in this case is a bigger problem than above. In this instance, the first step of the process could capture both of these and in the follow up email notify if the email entered is already taken and advise of the username already associated with the email. Not a major problem so far… but what if the email is available but the username is already being used by a different user.

On popular sites, millions of usernames could already be taken, so it would be impractical to send a ‘username already taken’ email everytime someone tries to use an existing username as it could take them god knows how tries to find a free one. In this case, the best balance between security and usability may be to notify the user trying to register that their desired username is already taken on the registration page itself rather than via email but also to reCAPTCHA enable the page so scripts won’t work. This means the task of trying to find if a username already exists requires manual intervention by the attacker which significantly reduces their ability to build lists of existing accounts. Of course reCAPTCHA from Google and similar plugins by others are not unbreakable so there is no 100% perfect solution here. Your specific implementation is likely to be yet another trade off between security and usability.

suggestions to prevent account enumeration:

  1. Use email addresses as usernames as this makes accounts easier to protect on password reset and registration pages.
  2. Show generic ‘Invalid username/password combination’ message on failed login.
  3. Show generic ‘Further instructions have been sent to your email address’ message on password reset. If email is already in DB, send a time limited reset password URL
  4. First registration step is user enters email address and submits, then the site shows generic ‘Further instructions have been sent to your email address’ message. If email is already in DB, email says already registered etc. If email not in DB, email should contain a continue registration URL.

related Links

Troy Hunt’s (my favourite microsoft MVP by a mile) amazing article ‘Everything you ever wanted to know about building a secure password reset feature‘ from 2012 which was how I was originally introduced to many of the ideas above.

Troy Hunt’s Secure Account Management Fundamentals PluralSight course. Another amazing resource from Troy.

OWASP page on preventing brute force attacks. Note in particular the section on account lockouts and the potential exploits that remain even when doing this after X bad attempts. The surface space of many of these exploits can be reduced by preventing account enumeration on login, reset and registration pages as per above.

please can I have your comments and/or questions?

I’m very interested to know your thoughts on the whole topic of account enumeration so please feel free to leave a comment. If you’re a developer or IT professional and would like to connect with me on LinkedIn please check out my profile and send me a connection request.

Converting numbers to strings without scientific notation in C#

C# will automatically convert numbers which are of type float, double or decimal and have a lot of precision (lots of numbers after the decimal point) to scientific notation. The means if you have a double which for example contains the value .00009 and attempt to convert it to a string C# will display it as 9E-05. Of course this may not always be desired. To ‘fix’ this you just need to explicitly format the string:

double number = .00009;
string defaultNumber = number.ToString(); //9E-05
string numberFromToString = number.ToString("N5"); //0.00009
string numberFromStringFormat = string.Format("{0:F5}", number); //0.00009

Change 5 above to whatever level of precision you require.