lundi 4 décembre 2006

The default design pattern

For a CORBA school project I made this pattern to deal with several bus. It is similar to the singleton pattern. This pattern is useful if you need the same instance in heighty or ninety percent of cases.

Imagine your application deal with one database. As a good programmer you write a connection pool. Each instance have a host, user and password attributes. After that, you decided to make this class a singleton because you work only with one database. Every thing works well, that seems perfect. Unfortunatly your application is growing and six months later it have to deals in infrequent cases with a second database. Damned, you can't reuse your connection pool because you need two instances of it and that's not possible because your pool is a singleton.

In this case the good solution is to have a default instance. Thus in most case you get the connection pool by the default_instance method . And in the other hand you create new instances. The first call of default_instance will create a new instance and the next will return the created instance. The sample code below show how this design pattern is simple.

require 'default'

class ConnectionPool
include Default # Add a default_instance methode
...
end

# Deal with the main database
pool = ConnectionPool.default_instance
# Deal with the second database
pool = ConnectionPool.new('host', 'name', 'pwd')


You can get the ruby source code here.

samedi 25 novembre 2006

The end of unsecure authentification

I don't know what's your opinion about clear passwords on the web but mine is clear. I hate typing them on unsecure channels. When I'm pressing the submit button I'm always thinking that some body can discover my password without much effort. He just needs a sniffer like ethereal and wait for some passwords to be caught by the soft. One solution is to use the https protocol instead of http. Unfortunately too few web site use it. But why? Because to use https you need to generate or pay for a certificate, you need to configure your server and you need more cpu. Even if this is not really hard too many web sites don't encrypt passwords between the client and the server. Maybe we need a solution which haven't https'constraints. I have a proposal.

My proposal doesn't require certificates, doesn't need any configuration on the web server and little cpu.

First we have to admit that good web sites don't store passwords in plain text in their database. It's often a hash sum like md5 for example. But if two users have the same password then the same sum will appear. So more serious sites concatenate a random seed to the password and then compute the hash sum. It should look like this: md5(password + seed). Well, instead of sending clear passwords we can send their hash sum. No! Because the bytes sent to the server will always be the same. Consequently the sniffer will not get the clear password but the data representing the password. That's just what he needs for connecting. We can do better.

To improve the system we need to generate a temp seed for each connection attempt. Thus by computing a double hash our password will be never revealed. Here is the formula.

md5(md5(password + seed) + temp_seed)

Why double sum instead of a simple md5(password + seed + tmp_seed)? Remember, in database passwords are stored in hash sum with a seed. Consequently the web site only knows the value of md5(password + seed). Thus from this value we just need to concatenate the temp seed, compute the sum and check if bytes are equal.

Our theory is good, we've done the half work. Now we will build our secure authentification system with Ruby on rails. Because I love Ruby and the rails framework rocks !

In our form we need several fields. Login represents the user's name, password where the user types his password, seed contains the user's seed, temp seed is the seed generated for each form, and password sum will be the result of the client side compute.

The main difficulty is to get the seed of the specified user. Indeed the seed is unique for each user but we need it in the form to compute the hash. Consequently we can only get it when we know user's login. Thanks to rails and AJAX a method called observer_field allow to send request when the value of the specified field changed. Thus severals request will be sent when the user is tiping his name to get his seed. In my example when login is edited a request is sent to the seed action and then a new field is put in the seed_container.

<%= javascript_include_tag 'md5' %>
<%= javascript_include_tag 'connection' %>
<%= javascript_include_tag 'prototype' %>

<p style="color: red"><%= flash[:error] %></p>
<%= start_form_tag 'check' %>
Login: <%= text_field_tag :login %> <br/>
Password: <%= password_field_tag :password %> <br/>
Seed: <span id='seed_container'><%= text_field_tag :seed %></span> <br/>
Temp seed: <%= text_field_tag :tmp_seed, @tmp_seed %> <br/>
Password sum: <%= text_field_tag :password_sum %> <br/>
<%= submit_tag 'Connection', :onClick => "hash_password('password', 'seed', 'tmp_seed', 'password_sum')" %> <br/>
<%= observe_field :login, :frequency => 0.5, :url => {:action => 'seed'}, :update => 'seed_container' %>
<%= end_form_tag %>


Finaly we need to compute the password sum before sending the form. As you can see in the source code sample the function hash_password (in connection.js) is called when the user click on submit.

function hash_password(pwd_id, pwd_seed_id, tmp_seed_id, pwd_sum_id) {
var pwd = document.getElementById(pwd_id);
var pwd_seed = document.getElementById(pwd_seed_id);
var tmp_seed = document.getElementById(tmp_seed_id);
var pwd_sum = document.getElementById(pwd_sum_id);
// Compute md5(md5(password + seed) + temp_seed)
// And don't forget to blank the original password field
pwd_sum.value = md5_hex(md5_hex(pwd.value + pwd_seed.value) + tmp_seed.value);
pwd.value = '';
}


This function is quite simple. The two last lines are the most important. The first compute the sum as explained before. And the last blank the password field to not send it over the network. As you may noticed I included un md5 file which isn't mine and can be found here. You also can use sha-1 instead of md5.

I hope that this note will help you to have your own secure authentification and/or you have been conscious of clear passwords over internet.

Get source code