This article was originally published in the February 2016 edition of the php[architect] magazine in the "Security Corner" column.
When writing good, solid (and DRY) code for your applications, we as developers want to make it the best that we can. Depending on the library or feature that we're creating, this can mean a lot of different things. Usually, though, this includes flexibility. We want to write code that can hold up over time, can roll with the punches and adapt to the needs of the rest of the codebase without too much extra effort from developers down the line. We happily build this flexibility into the code and make it easy for other developers to custom-tailor the objects they need with a few constructor arguments and method calls. Sounds like an ideal world, right? Well, in most cases it is but there's one context where you could be doing a detriment to the users of your library or tool....you guessed it: security.
If you've been a reader of this column so far you know that I've talked about some of the shifts in thinking it takes to integrate more secure development principles into your every day code. With the creation of most other kinds of tools and features, having as much flexibility as possible (within reason, naturally) is a good thing. You give developers all the access they might need to the inner workings of your tool with getters, setters and injection points. They can craft a custom setup that's just exactly what they need at any given time. As the man once said, however, with great power comes great responsibility. By providing all of this flexibility to a user you may be providing them with functionality that could lead to the compromise of their application.
Let's look at an example - a basic encryption library. Encryption itself can be a rough topic to try to cover in a an article like this, so I'm only going to use it as a backdrop here. I'm not going to get into guidance on what kind of crypto is good or bad and give advice on what you should use - that's for another time. Instead I want to use this as a platform to talk about one of the best things you can do for those that would be using your software: establish secure defaults.
Let's create our basic encryption library and give two basic methods:
decrypt. In the back of every developer's mind right now there's probably this nagging voice looking at the next steps past these two obvious methods. It wants to implement the rest of the class so a user can select whatever algorithm and mode they'd like to use when performing the encryption and decryption of the data provided. Pseudo-code starts going through your mind, maybe with constructor arguments for each of these different types and a little logic internally to handle whichever the user selects. There's just one problem here - if you're the developer that knows about encryption and are making this library for others to use, why aren't you giving them any guidance?
While the main point of writing open source software and releasing it out to the world is to make things that work, there's also a value in seeing how something is done. Developers create theses tools for a reason - to solve problems - and in solving them they usually figure out the best practices. They then take this knowledge and put it into the library so others can benefit from the efforts and not have to make those same mistakes.
Now, back to our encryption library - if we allow the user to define whatever algorithm/mode combination they'd like, they're not benefiting from our experience in working with the encryption of our own application. We've already solved the problem for our systems so we have a set of "known goods" we can share with the rest of the world via our code. So, instead of making the library uber-flexible and leave it up to the user to decide what's good or not, be direct with them and tell them....not in documentation but in the code itself.
Instead of offering direct access to the algorithm/mode combination, create a series of "levels" of complexity the user can pick from based on how strong they need the encryption level to be. This could even be as general as a "low", "medium" and "high" set of choices, possibly defined as something like a class constant or changed via a method call. The key here is that this grouping of defaults provides the end user with some "known goods" to work from. They get to stand on your shoulders and use what you've learned to make their application even more secure and with a minimum amount of effort.
One thing to keep in mind when creating these defaults too is that they're not always going to be popular. The needs of some end users may not exactly match these carefully selected defaults you've created and it may get a little frustrating for them. Keep this in mind and, depending on what kind of functionality you'd like to provide, maybe allow a more advanced user to manually tweak the settings an alternate way rather than the simple default-driven method. However, be wary of including something like this - that kind of functionality has a way of being abused.
This is the most tricky part of the whole thing - it entirely depends on what you're trying to accomplish. In my example I've been talking about encryption functionality since the idea of "levels" transfers over easily to that world. What if you're not creating an encryption library with such clearly defined "levels" you can rely on? Here's a few suggestions of questions you can ask that could help you determine what these levels might be in your code:
Ask yourself these questions and think about the "chunks" of your library or application and see if there's any lines that could be drawn to help split it up. Share what you've learned in developing the code and help others secure their own apps even more effectively.
With over 12 years of PHP experience and a focus on application security Chris is on a quest to bring his knowledge to the masses, making application security accessible to everyone. He also provides application security training and consulting services.