Secure Coding by Default

Two talks at RSA last week helped galvanize a concept for me that has been kicking around unverbalized in my head. I have long thought that things like CSRF are problems that developers *shouldn’t* need to worry about – they are a fundamental design issue with the web that requires custom coding in each application to rectify. This holds true for many (all?) of the other implementation level issues as well. Our strategy for application security is to make developers aware of all of the potential problems and put the responsibility on them to code around the problems because we haven’t come up with a better option (but we have, and I will get to that). The art of writing code is simultaneously seeing the big picture and the small picture – understanding how each individual statement builds towards bigger functionality. That alone is a fair amount to simultaneously track, and every concern or complication that the developers have to be aware of at the statement level increases the chances that they will make a mistake.

Brian Chess (of Fortify Fame) gave a great talk last Monday at RSA as part of the AppSec symposium, where he discussed why Buffer Overflows still exist even though we know the technical cause. He had a great demonstration of how many decisions a developer needs to make in a single line of code to get some derivative of sprintf to be safe from an overflow (it was either 6 or 8 decisions, for what amounts to a pretty simple buffer transfer). That was the eureka moment for me, where my own thoughts came together – the cause of Buffer Overflows isn’t buffer manipulation per se, but rather that the developer needs to simultaneously be aware of many conditions and get every single one of them right. How many other implementation level vulnerabilities are the same way? We know that managed languages mostly cure buffer overflows (edge cases like the C# Unsafe keyword not withstanding) but I think we chalk up the reason why to purely technical concerns. What we miss is that the technical concerns are a result of a coherent design decision – automate the memory allocation for strings so the developer never has to be aware of it.

The second talk that reinforced this was by Alan Karp of HP Labs. He was demonstrating an analog to email file sharing that hid all of the security decisions from the user; his little app automatically used SSL for all communications, automatically applied ACLs to the files being shared, automatically had user authentication built in to restrict file access, etc but look to the user just like the normal workflow for sending a file via email. In practice I don’t think we need one more way to share files, but as a demonstration of security concepts, where security is implicit rather than explicit, it worked.

Right now AppSec is very tactical. We are trying to fix the immediate problems through developer education and add on security frameworks, and as a stop gap that’s fine. For a strategy though, I think the various custodians of the languages/popular frameworks (MS, Oracle, SpringSource, the Apache Foundation, the PHP group, etc) should make a conscious decision to change the languages/frameworks to hide the default security decisions from developers in the exact same way that the move to managed languages hid buffer decisions. Add whitelist validation implicitly to the various parameter retrieval functionality based on the current character set, so that every time the developer accesses a parameter it is automatically sanitized (as an aside, I think languages should not automatically mix retrieval from Cookies/Get/Post variables). If the developer wants to deviate from the implicit input validation then have an unsafe version of the retrieval mechanism that they have to explicitly invoke (in the same way that you have to explicitly use the Unsafe keyword in C# for memory manipulation); that would still allow the current flexibility but it would make such a choice explicit for the developer and make flagging areas for manual code review very easy. Replace the current SQL with a query language completely immune to injection attacks (essentially something like LINQ); yes we have parameterized queries now, but they are an opt-in solution. I think languages or frameworks should instead move to opt-out solutions. Take the notions of ViewState the full distance to prevent CSRF (since they only *mostly* protect against CSRF now, and are obviously MS specific) and make that the defacto mechanism everywhere. And so forth for as many of the implementation level issues as we can. So long as we put the burden on the developer to get everything perfect they will continue to miss things, because humans just aren’t perfect. We need to move a lot of that burden to the languages or frameworks the developers are using, just as we did to get rid of Buffer Overflows.

Yeah, it will break a whole lot of backwards compatibility. Yes a migration path will be hard and it will only work if we can get people to migrate. But in yet one more RSA there continues to be a whole lot of talks about how we are still losing at AppSec (with folks like Verizon providing hard data to prove it), nothing but false promises from vendors to fix the problem, and the best strategies (education and verification tools) being no different from 5 years ago. I think more drastic and ambitious attempts are really in order.

~ Joshbw

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>