I have always developed with security in mind. I develop clean code with reasonable capability. I practice the philosophy of the “Principle of Least Access” at all times. I unit test, getting as close to 100% code coverage as possible with all boundary conditions checked. I do periodic intrusion testing, load testing, testing, testing, testing… Somehow, however, some way, there always seem to be holes.
Not holes that a user can exploit, mind you, but ones that I can exploit. I always imagine myself as my own customer. I am a DBA, locking down access to only the least access necessary for everyone as I create stored procedures, views, functions, and the like. Then I imagine myself as a “customer” of that database: the object layer developer. I should not be able to, as the object layer developer, get to the guts of the database. I should also be making all methods that are only locally used “private” or “internal” to the library. I then imagine myself as the “customer” of this object library; as if I bought this library from a vendor and am not allowed to know the guts of the library. I use the library to make my application, never even allowed to “know” the mechanism used for persistent storage (the database) obscured now two layers down. This has always worked well for me.
Nowadays, I add APIs to most of my applications, and need to then again make calls appropriately to my classes to make sure I am coding securely. Most of my applications are multi-account applications with all data in a single database and filtering by account ID. This is where the holes tend to be. If I ask to load a resource by ID, I have to make sure that the person asking has access to that resource. Previously, I did that on both the API and the application, and very occasionally, a poorly written unit test would fail to catch any problems here.
A few years ago, I decided to change the philosophy a bit when we created an application specifically based around privacy and security. We were working closely with financial institutions, credit card companies, and data warehouse companies, and needed to look as legit as possible. It’s not that we weren’t, we just wanted to include in our literature some good language about our application security.
As a result, we added a specific LAYER to our development model for security. First step, I set the scope of EVERY SINGLE method on EVERY SINGLE CLASS in my library to internal to the library itself. I then created a security model. The model was represented by a state object, initialized with a specific user. We named it the “PublicModel” (not as in MVC Model, but as in Security Model). Here is an example of a constructor:
The expectation is that, for the application, I initialize this object on login to the application, then store the application in state (Session, ViewState, static memory, whatever) and I never have to initialize it again during this user session.
All of the methods on the model object are written like an API. In this example, I want to load a Job by ID.
I then take care of all security in the private method “LoadJob”.
ANY TIME AT ALL I want to load a job in this class, I do it through a call to LoadJob(). I am guaranteed access restriction is maintained.
I create methods on the PublicModel for EVERY SINGLE interaction the Application requires. Then, once I am done, I can create my API directly, one for one, from this PublicModel class. After all, I have heard that an API should be useful enough that you could re-create the website from it. Well, not only CAN we do that, we DO.
Finally, this methodology came with an unexpected advantage. When I create a class library, I tend to go a little overboard and create my methods, not knowing how quickly they will become obsolete. Now, I don’t even unit test my classes directly, rather I unit test the public model. Anything that is not covered by my unit test is automatically useless, and I can delete it. I have now streamlined my class library to only what is useful. (One caveat – I DO actually do SOME unit tests to the class library still to cover ALL possible scenarios so I don’t have to test them all through the public model.)
All in all, this has become a great tool for us. It has made us more agile as far as developing for both applications and our APIs, and it has made everything more secure and more consistent.