Application security is a hot topic. No one wants to write code that leads to the next data breach or major headline. C# security is on the minds of many .NET developers.
Secure coding involves detailed knowledge of a programming language and framework. There could be dozens of rules articulated here with detailed code examples showing right versus wrong coding practices. However, that might lead to quite a bit of dry reading and low retention. Instead, I’ll take a different angle. I’ll describe three principles that lead to strong, secure coding practices. Learn these principles well, and you’ll be able to secure any application you build.
C# Security Principle #1: Understand the Details (and Dangers) of Your Chosen Framework
C# security isn’t just about the language itself. Remember that there’s a framework and class library behind it. Security issues can arise when developers use features of the framework without understanding how they can be abused. Let’s look at a couple of examples.
ASP.NET MVC Model Binding
In MVC applications, model binding is the mechanism that maps incoming form or JSON values to object properties. For example, imagine you have a “to do” list application that has a ToDoItem class with two properties: ItemId and ItemDescription. Model binding will automatically search inputs (form inputs, parameters, and query string parameters) for ItemId and ItemDescription values and map them to an instance of ToDoItem. If there’s a REST service for the list, you can pass in an ItemId and ItemDescription with their respective values, and everything just works when it reaches your controller.
Now imagine an application that has normal users and admin users that are instances of a User class. The IsAdmin property determines whether a user is an admin or not. If your controller method takes a User object as an argument, then model binding will do its magic. Only this time, it’s dangerous. Even if the IsAdmin value isn’t returned to the UI, an attacker can intercept the request and add a simple “IsAdmin: True” to the JSON payload sent to the server. Model binding finds the IsAdmin property on the object, maps the value, and—just like that—the attacker is an admin on your site.
The mitigation for this threat is quite simple. Use view models to communicate back and forth from the client and server. View Models hold only the data necessary for the current operation, not all of the data in a domain object. The controller method takes a UserViewModel object as an argument, and the IsAdmin property isn’t there. Now when the attacker adds the IsAdmin key/value pair to the request, he doesn’t become an admin.
Understand Your Defaults
C# security is intertwined with the .NET framework for the most part. The great thing about having a framework backing you up is there are many sensible defaults that help you stay secure. Now that ASP.NET Core 2.0 has been released, the framework does even more for you. It’s your job as a developer to understand what defaults exist and how to make sure they aren’t compromised.
ASP.NET MVC also has great built-in cross-site request forgery (CSRF) protection. Simply annotate controller methods with ValidateAntiForgeryToken, and the framework handles the details. ASP.NET Core has made it even easier. Now you can add an AutoValidateAntiforgeryTokenAttribute filter in the ConfigureServices method to automatically add the CSRF protection to all controller methods that need it.
The bottom line is this: do the research on the framework and tools you’re using and on how they can protect your users. Use the tools provided to build secure applications, and create standards for all developers on the team to follow. Your applications will be much more secure just by understanding what the .NET framework provides and using it effectively.
C# Security Principle #2: Never Roll Your Own Cryptography
There is one security truth 99% of developers should take to heart: you do not know how to make a secure cryptographic algorithm. You’d be surprised how many software development teams try to create their own cryptographic algorithm. Don’t be one of them. Stick to the algorithms that have stood the test of time and the scrutiny of cryptography experts.
The .NET framework is your friend here as well. There are plenty of .NET framework implementations of strong encryption algorithms. I have written multiple posts about cryptography for those that want a deep dive on which algorithms to use and why. Suffice it to say for now that you want AES for protecting sensitive data that rests in your database. The .NET framework has implementations of AES for use by developers so you don’t have to try to create your own.
Data Protection API
The .NET Core 2.0 release brought more goodies to developers in the realm of cryptography. Microsoft has added the Data Protection API to make it even easier for developers to use strong cryptography to protect their data. I personally love this API because it’s designed well from a security perspective and an API perspective. With this API, when you need to encrypt data, you simply pass the data into the Protect method. When you need to access the data again, simply pass the encrypted data into the Unprotect method, and it’s turned back into plaintext.
This API is great because it’s simple and successfully abstracts all of the inner workings away from the developers. By default, it uses 256-bit AES encryption to protect data, which is one of the best choices for an algorithm. When you encrypt data, key management becomes a concern. The Data Protection API handles all of that for you, including rotating keys on a regular basis. Developers don’t have to worry about the details, just what methods to call and when.
C# Security Principle #3: Automate, Automate, Automate
For any C# security program to be successful, you need to deliver code quickly while still having confidence in the security of your code. There’s less and less tolerance for security folks stopping all development to perform reviews of the code. Automation is the key to delivering secure code quickly.
There are two ways to build automation into security. First, create automated security test cases that can be run with every commit and build. These tests verify that basic security controls are in place. Write a test that attempts a CSRF attack, and make sure it doesn’t work. Test your authentication and authorization mechanisms to make sure they’re rock solid. Especially with REST services, ensure that you’re returning HTTP error codes such as 401 and 403 so you can clearly test and prove that these controls are working. Write tests that check for headers such as Content Security Policy or HSTS. These simple checks can make security a daily part of your development cycle. If your build fails because of one of the tests failing, you’ll know about it immediately and fix the bug before it reaches production.
Second, you can bring automation even further “left” into the development lifecycle by using tools that integrate directly into the developer’s IDE. The Roslyn project allows teams to create code analyzers that will find issues while the developers write code. Use these to create custom rules for your application.
Of course, that can be quite a labor intensive. So instead, use pre-built tools that you don’t have to write yourself. One such tool is SubMain’s CodeIt.Right, which provides great tips to developers as they code. These tips include many security best practices, as well as general coding guidelines. You can also customize CodeIt.Right with your own rules, including security rules. When you do the research I spoke about previously to find security guidelines for your team, incorporate them into CodeIt.Right to help educate developers as they code.
Shoot for Principles, Not Laws
Laws are hard and fast rules you must follow. Principles are higher level truths and practices that guide your decisions every day. A rule is “Don’t return user-provided data to the client without encoding.” On the other hand, principles will provide a strong foundation upon which to build your C# security program, regardless of what specific attacks you need to protect your application against.
The three principles we covered here are
- Understand your framework.
- Don’t try to roll your own cryptography.
- Automate your security practices (including the rules) as much as possible.
When you establish and follow principles such as these, you’ll set yourself up for success no matter what application you’re building.