Taking a peek under the hood

BAFTA Kids

Each year in additional to the main BAFTA awards there is also the annual Kids Vote where children are encouraged to vote online for their favourite film, TV show and computer game.  This was our first major piece of work won via tender as well as the first site we'd designed with children as the target audience - no pressure then!


My involvement in this project was actually rather limited as it was developed largely by our in-house development team with me simply providing daily code reviews and parachuting in to help debug issues for our developers.  

The only major piece of functionality I worked on for the site were the features that ensured the integrity of the vote. This was something that was obviously extremely important and although I can't imagine Paddington trying to rig the vote it had to be given serious consideration. During the build we considered lots of different techniques including:

Cookie based protection

When a vote is placed you drop a cookie which we use to update the UI so the same person cannot vote again.  This is mostly a UI consideration and will probably provide a decent level of protection against children voting on school computers:

function bafta_cookie_check( $form, &$form_state )
{
  if ( isset( $_COOKIE['bafta_kids_vote_' . date('Y')] ) )
  {
    raise_form_error( t( 'cookie_present' ) );
  }
}

Obviously this only offers protection at the very lowest level as clearing cookies, going incognito, using a different browser or a more sophisticated replay attack would easily bypass this protection - in the search for something more robust we also considered the techniques below.

Time based restrictions

When someone fills out the form 'normally' there is a minimum amount of time required to realistically vote correctly.  Someone simply reloading the page and filling out as quickly as possible (or mounting an automated attack) would complete the page far quicker.  So if we store the time the form is first loaded and then compare this against the current time on submission we can calculate how long the user took to complete the form and filter out a lot of malicious submissions: 

function bafta_speeding_check( $form, &$form_state )
{
  if ( isset( $_SESSION['nonce_generated_at'] ) )
  {
    $time_elapsed = time() - $_SESSION['nonce_generated_at'];

    if ( $time_elapsed < REASONABLE_COMPLETION_TIME )
    {
      raise_form_error( t('too_fast', array( $time_elapsed ) ) );
    }
  }

  unset( $_SESSION['nonce_generated_at'] );
}

Nonce based restrictions

This technique is largely focused on protecting against malicious users capturing the POST request made during a normal vote and then simply replaying this POST request back to the server.  What you could do is each time you load the voting form you generate a random string which is stored server side and embedded in the form.  When the form gets submitted you check that the nonce value submitted via the form matches that in the session before proceeding to place the vote.  Something like this:

function bafta_nonce_check( $form, &$form_state )
{
  if ( ! isset( $form_state['input']['nonce'] ) 
    || ! isset( $_SESSION['current_vote_form_nonce'] ) )
  {
    raise_form_error( t( 'no_nonce' ) );
  }
  elseif ( $form_state['input']['nonce'] !== $_SESSION['current_vote_form_nonce'] )
  {
    raise_form_error( t( 'invalid_nonce' ) );
  }

  unset( $_SESSION['current_vote_form_nonce'] );
}

IP based protection

This technique would aim to protect against relatively unsophisticated automated attacks (i.e. without IP spoofing or using a botnet) and would simply log the IP of the user making the request and then during a submission check if the IP address making the current request and check it had not exceeded it's quota:

function bafta_ip_check( $form, &$form_state )
{
  $users_ip = $_SERVER['REMOTE_ADDR'];
  $previous_votes_from_ip = bafta_votes_from_ip( $users_ip );

  if ( $previous_votes_from_ip > $votes_allowed_per_ip )
  {
    raise_form_error( t( 'ip_limit', array('ip' => $users_ip ) );
  }
}

While on their own each of these would provide relatively little protection however in combination they would provide a relatively robust layer of protection and help to ensure the integrity of the vote - essentially defence in depth.  

I guess the only other major thing you might want to consider if faced with a similar problem is some form of CAPTCHA but given this was a site designed with children in mind it seemed like it may impact the usability of the site too much.