When it comes to application vulnerabilities, there are 2 common groups we might view them: Technical Debt and New Development.
I break these down because the way in which we address vulnerabilities is fundamentally different. Something that might not be exploitable receives a very low priority when it is technical debt. However, during new development, it can be addressed with little cost. Much of this comes down to if soemthing is syntactically insecure vs. actually vulnerable. Let’s look at a few examples.
Example 1: Output Encoding (XSS)
Syntactically, any data sent to the browser should have proper output encoding applied to protect against Cross-Site Scripting. At the most basic level, if I am going to write out some data, that data should be properly encoded. The actual exploitability changes based on where that data is coming from. This could range from a local constant, a hard coded string, a file, request parameter, or a databse.
In any of these cases, the argument could be made that the code is not written in a secure manner. Even in the case of a hard coded string, because what happens if later on a developer changes that code to pull the data from a different source? Will they remember to apply the proper encoding? On the other hand, the code isn’t exploitable at this time. This is where that difference comes in.
When writing new code, if you have a static analyzer to help identify these issues, or strong secure coding policies, you would hopefully catch this during a peer review or pull request and the cost to resolve it is pretty much nothing. As a matter of fact, even applying the encoding to a static string helps drive writing secure code by default. Now, if someone changes it in the future, it is already properly encoded. There are few good reasons to not resolve this type of issue right then and there, vs adding it to the back log (technical debt).
Contrast that with technical debt. Most of the technical debt comes from previously written code through some sort of scanning solution. For example, static analysis will most likely identify a lot of existing potential vulnerabilities. The cost to go back and fix all of the identified issues will be much higher. In addition, many of the identified issues may be like the static string, something that is not vulnerable but syntactically insecure. This is where triaging and prioritization comes into play to determine what to fix and when. This process of vulnerability management comes at a pretty significant cost as we now have to have resources and tools in place to manage these issues.
Example 2: XML External Entity (XXE)
As another example, let’s look at parsing XML. There are many sources we may get XML from. The risk, of course, is when the XML contains External Entity references that the user can control.
One could argue that XML that is not controllable by a user wouldn’t be vulnerable. For example, many people would trust XML if it came from their database. Unfortunately, this is not a trusted source when it comes to potentially malicious data. It is not uncommon to close vulnerabilities like this because they are not exploitable in their current form. While syntactically, the XML Parser being used may parse DTDs, the actual XML is not vulnerable.
When doing new development, it should be a requirement that the XML Parser is configured to not parse DTD by default. It shouldn’t matter where the XML is coming from. DTD processing should only be enabled in the rare case it is needed. When working through the backlog (techncial debt), you might find yourself closing or lowering the priority of instances where the XML DTD can’t be controlled by the user or outside party.
Wrap Up
In many cases, technical debt is way more time consuming than when we are fixing things as we write the code.
The goal should be that we are not continuing to add to our technical debt as we create new development. Write things in a default secure manner, no matter the source of the data. This takes the guesswork out of which things to fix or not fix and instills a secure by default development practice. It changes the way development is done so that the secure way is the default way and anything else looks out of the ordinary.Tr