Last week I attended Tech Ed Australia, and as usual at Tech Ed, I found it impossible to stick to one track, instead picking and choosing interesting bits and pieces from almost all of the tracks. One thing that I am starting to get realliy interested in is Security, particularly code security. One thing that I don't think a lot of developers a really aware of is that you can have the best infrastructure security money can buy, you can have fire walls, and DMZ's set up so tight that no un-authorized traffic can possibly get through, but it just takes one bad line of code to blow a whole so wide in your defenses that it can render these measures meaningless.
The buzz these days is on Defense in Depth, which roughly translated means having multiple layers of checks and safe guards so that if a bad line of code up stream lets an attack through, there is a high probability that code further down will pick it up and dispense with it appropriately. I attended a number of security seminars at Tech Ed, and I'd like to expound on a concept that I have have been using in my current role, and it was really good to hear it codified into three basic development pratices at Tech Ed.
The three pillars of this approach to code security are Constrain, Reject and Sanitize, and I want to explain in this series of articles how these concepts fit in with day to day development, and how they can form part of a defense in depth approach to your products.
I'll start in this article by explaining what the three trems mean, I'll then go on and spend an entire article on each of these three concepts and explain how these can fit into the software development life cycle.
At the root of almost all code level attacks is user input. SQL Injection exploits the single quote (') special character in the SQL language, Cross Site Scripting (XSS) exploits the web browsers propensity to want to render HTML tags. Generally speaking the developers of the system never intend the input to be in this format in the first place. The constraining of data should be done at the point where the user is entering the data into the system. This is commonly referred to as "white list" checking.
There are certain known common attack vectors that should be rejected out right. If you are accepting input from a web site comment field, and the user types in "<scrpt>...</script>" chances are you are not really going to want that users comment, and the safest thing to do is to reject it, and tell the user that the input was not acceptable. The rejection of suspect data can be done at multiple phases such as code layer boundaries, or even as an aspect of the way your system works. This is commonly referred to as "black list" checking.
Sanitizing the user input is what I like to call the last line of defense. This is usually done just before user input is presented to the attack target (ie SQL Server in the case of SQL Injection, or the browser in the case of XSS). Sanitizing information is usually done by escaping potentially malicious data before presenting it. As an example, in the case of XSS where the browser is the attack target, a string such as <script>...</script> is rendered harmless if it is HTMLEncoded to <script>...</script>.
Putting them all together
By now you may be able to see the idea of "defense in depth" starting to form. In the first place, we constrain, only allowing data that we expect to be entered. Secondly we reject any known attack vectors so even if we can't constrain every field on in our application (or a lax developer forgets to constrain), we can at least protected ourselves against known attack vectors, and finally if constraining and rejecting fail to pick up a potential attack vector, then sanitizing the output will render the attempted attack harmless.