We specialize in security and compliance for the health care and bio-med space, helping clients build security into their products, instead of bolting it on later. There are plenty of challenges to go around and it often seems like you’re trying to drink from a fire-hose. Lots of water, a few drops into your mouth, getting thoroughly wet and having lots of fun!
This week, we had oodles of interesting problems to solve but the 64K question is:
How many of the solutions will we remember next week this time?
I decided to start writing a weekly lessons_learned file. I’m maintaining it in my own git repository. If I go anywhere with some of these daily lessons, I’ll move it to github. Here are a few lessons I learned this week.
- A little bit of introspection can go a long way – or how not to make changes in a complex Web application and reduce the probability of introducing new bugs and new vulnerabilities.
- Do not take Unicode for granted
- Making MVC frameworks more secure with stored procedures and views
- Denormalization is not a bad thing especially if it keeps the code simple
- Data validation: replacing declarative statements with Javascript code
Lessons learned 18/8/2011
1. A little bit of introspection can go a long way
We were trying to extend some functionality in a big health care Web application UI without introducing new vulnerabilities and without breaking existing functionality. The application uses grid layouts with a CSS framework.
$this->renderLine and $this->renderHeader have hard coded grids from 2-8 columns. The number of columns was a passed argument to the method that specified the number of columns and a layout of css grid columns.
By making a very small change, counting the number of columns in the array, we enabled generalization of the grid models without changing existing code.
For example:
Before:
function renderHeader($n_cols,$k,$field, $display_sidebar = true){
switch ($n_cols) {
….
}
After:
function renderHeader($column_format,$k,$field, $display_sidebar = true){switch ($column_format) {
case (“71”):
$layout = array(6,2,2,2,2,2,2);
break;
case (“7”):
$layout = array(3,3,3,3,2,2,2);
break;
}
$n_cols = count($layout);
2. Do not take UTF-8 for granted
I supposed we all assume these days that Unicode is just built-in. But globalizing a Web app may result in some of the pages being rendered with gibberish. I remembered my Russian lessons:
If you want UTF-8 characters to render on your page make sure you include this in the header, place it in the page layout under the <head> tag
<meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8″ />
3. Using stored procedures in MVC frameworks makes them safer and simpler
You’re using one of those fancy MVC frameworks like RoR or CakePHP. If you’re using standard models with joins that ok, but once you start getting into more complex stuff like distinct and group by and union statements the model and the controller becomes more complex – SQL injection vulnerabilities may start cropping up.
You know that’s the case when developers start spending a lot of time trying to figure out how to implement Model->find statements and bypassing the framework with manual SQL code. The solution is that its’s easier to make a logical view and let the database do the heavy lifting. The model then becomes trivially simple, like this:
class Offer extends AppModel {
// Logical view of packages that can be offered to a customer including those she already purchased
var $name = ‘Offer’;
where offers is a logical view, compiled into the database (for example the excellent PostgreSQL FOSS database)
No query string parameters to threaten the application. It’s baked into the back end database.
MVC frameworks with PHP, Ruby or Python are great but because they’re interpreted languages and provide no type guarantees in the interface to the database, there can be serious security vulnerabilities and buggy code waiting in the wings of your code just waiting to be exploited.
This where I think PostgreSQL and PL/pgSQL shines. PL/pgSQL is compiled and type safe. In those cases where you need to do more complex views or computations – I take it out of the PHP, Ruby or Python code and implement functions, stored procedures, triggers and logical views. It’s one more programming language to learn but once you have it, the interpreted code becomes simpler, more reliable and more secure.
4. Denormalization is not a bad thing
Sometimes it’s just a lot better to denormalize. You started your data model with strict normalized tables and foreign keys, for example, a Package model that belongs to a Product model. As the code development progresses, you run into business requirements to snap shot Product base class information into the Package model. Welcome to the real denormlized world. Not a big deal and guess what, the queries are simpler and faster too because everything you need is in the Package model.
5. Data validation: replacing declarative statements with Javascript code
MVC frameworks like CakePHP and Ruby on Rails have powerful systems for model validation. I for one, have always found the syntax pretty kludgy (although definitely logical) and hard to remember. Besides the kludgility – the field validation process refreshes the page and writes the error inside your view, which is ok but maybe not your first option for an error response.
I’ve found that using Jquery code instead of declarative Model validation improves the responsivenss of the application and it’s a lot more fun to write some Java script than jumping through declarative Ruby on Rails validation rule hoops like this:
class User < ActiveRecord::Base
validates_uniqueness_of :username
validates_presence_of :username, :password, :email
validates_size_of :username, :password, :within => 5..15
validates_format_of :email,
:with => /^([^@s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})$/i
validates_format_of :username, :with => /^w+$/i,
:message => “can only contain letters and numbers.”
validates_confirmation_of :password
validates_inclusion_of :gender, :in => %w(male female)
validates_acceptance_of :eula
end
You have a form with terms and conditions. You don’t want users to bypass the click agreement. Note the use of the Jquery submit selector and checked attribute.
$(document).ready(function() {
$(‘input[type=”submit”]’).click(function() {
var cc = $(“#SubscriptionConfirmConditions”).attr(“checked”);
if (cc) {
return true;
} else {
alert(“Please confirm the terms and conditions”);
return false;
}
});
});