Security is a pretty important topic in software development. When developing software it is easier to leave your application insecure and open to attacks than to take security measures. Sometimes, I am guilty of slacking off when it comes to security. 😅😅
In this article, I'll be using the ASP.NET MVC template as the talking point. The MVC template is very weak/vulnerable, security-wise. One can say the template favors simplicity over security.
Here are a couple of tips on how to secure your ASP.NET MVC application
1. Hash passwords
I know this is sort of an obvious practice in software development. However, I still feel the need to mention it. Hashing passwords is a very crucial part of authentication, but quite an easy one to forget, especially if you're writing your own custom authentication code.
Also, choice of hashing algorithm is important. NEVER use MD5 to hash passwords, MD5 is super-easy to crack.😒😒
2. Use strong password validation logic
Try to make your password validation as 'strong' as possible. Below is my personal favourite when it comes to password validation.
UserManager.PasswordValidator = new PasswordValidator
{
RequiredLength = 6,
RequireNonLetterOrDigit = true,
RequireDigit = true,
RequireLowercase = true,
RequireUppercase = true,
};
3. Brief authentication error messages
Make your authentication error messages as short as possible. For example, on login failure, your error message should read "username/password invalid" rather than specify which is invalid, because that'd give enough information to a potential hacker.
Also, in password reset, if the entered email doesn't exist in the system, rather than return an error message, display a success page.
4. Turn on custom errors
By default the template shows full stack trace of exceptions whenever an error occurs. This is very sensitive information that should be hidden. This can be done from the web.config file by adding the custom errors tag and setting its mode to on.
<system.web>
<customErrors mode="On"></customErrors>
</system.web>
5. Secure your cookies!
By default, the template's cookies can be accessed by JavaScript from other sites. These cookies can also be sent unencrypted over the wire. This can be fixed by adding the httpcookies tag to the web.config file.
<system.web>
<httpCookies httpOnlyCookies="true" requireSSL="false"/>
</system.web>
If you're using SSL on your site, then requireSSL should be set to true.
6. Get rid of needless headers!
By default the template sends a couple of HTTP headers with every request. These headers contain sensitive information such as the server type, ASP.NET version and MVC version. We can fix this by setting enableVersionHeader setting to false.
<system.web>
<httpRuntime targetFramework="4.5.2" enableVersionHeader="false"/>
</system.web>
After doing this, we clear custom headers
<system.webServer>
<httpProtocol>
<customHeaders>
<clear />
</customHeaders>
</httpProtocol>
</system.webServer>
7. Rename your cookies.
In the MVC template, there are three basic cookies: the Application cookie, the Antiforgery Token and the Session cookie. We should rename these cookies to make the type of server we're using unknown to the user.
Renaming the session cookie
<system.web>
<sessionState cookieName="custom_name" />
</system.web>
Renaming the Antiforgery token
This is done in the Global.asax.cs file
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
//added line
ConfigureAntiForgeryTokens();
//end of added line
}
//added method
private static void ConfigureAntiForgeryTokens()
{
AntiForgeryConfig.CookieName = "custom_name";
// AntiForgeryConfig.RequireSsl = true;
}
//end of added method
If your site will use Ssl, uncomment the last line.
Renaming the Application cookie
This is done in the Startup.Auth.cs file of the App_Start folder
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
CookieName = "custom_name"
});
}
8. Disable tracing
<system.web>
<trace enabled="true" localOnly="true"/>
</system.web>
9. Display 403 - Forbidden response as a 404
When you navigate to a directory in IIS and ASP.NET MVC, it causes a 403 - Forbidden response to be returned. This basically means that directory browsing is disabled. Directory browsing is a security risk, as it grants access to the web.config file with all your connection strings.
When directory browsing returns a 403, it poses a problem as it tells a potential attacker that you're using IIS and there actually is a folder there.
We fix this by replacing the 403 response with the standard 404.
First we add httpErrors to the web.config file.
<system.webServer>
<!-- Custom error pages -->
<httpErrors errorMode="Custom" existingResponse="Replace">
<!-- Redirect IIS 403 Forbidden responses to the not found action method on error controller -->
<error statusCode="403" subStatusCode="14" responseMode="ExecuteURL" path="/error/notfound" />
</httpErrors>
</system.webServer>
Then we turn off IIS' default document handling. This stops IIS from returning the default document (Using whats called a courtesy redirect) when navigating to a folder e.g. navigating to ‘/Folder’ which contains an index.html file will not return ‘/Folder/index.html’. If it redirects to the default document, in the page url, it displays the directory's path to potential attackers and we wouldn't want that.
<system.webServer>
<defaultDocument enabled="false"/>
</system.webServer>
Using the few tips above, you can make your ASP.NET Application more secure.😠Thanks for reading! 😄🙂
Top comments (5)
There's some interesting research from Microsoft (and I think similar from the UK government) that suggests you should only enforce an 8 character length rather than all the symbols and numbers etc.
There is more explanation out there but from the top of my head it was mainly because people just make simple additions (like adding a '1' at the end) or substitutions (like '0' rather than 'o') which are trivial for password crackers to circumnavigate. They instead suggest checking the password isn't one of the most common ones to stop things like 'password' or 'qwerty' being used.
There's an xkcd comic about it that I'd find if I wasn't on my phone! 🙂
You're right. While enforcing complexity does increase the search space, it also eliminates a huge search space (i.e., all the possible combinations that do not require the required complexity). It also isn't as easy to remember, which incentivizes users to store them somewhere else; this greatly reduces the effective security of the user's account. Sure, they're still hard to brute-force, but if you can obtain it out-of-band, there's no brute-force required.
I learned some techniques, too, from this post (cookie renaming and 403 -> 404). Great job, Banso!
Thanks for the insight Jamie. I just learnt something new.
Thank you for a good writeup.
Yes, better use long passwords than short passwords with digits and symbols. This is probably the xkcd, Jamie Read is referring to:
xkcd.com/936/
Thank You! The xkcd explains greatly.