Best Practice: Sites and record identifiers

Now that Force.com Sites is out in the developer community as a preview, we are starting to see several interesting and powerful use cases that involve generating forms that will input data into the Force.com database.  As these use cases come up, we are often asked is it acceptable from a security perspective to allow the Sites Public User to input data directly into the database ? 

The answer is Yes, however as a developer it's important to understand there is a safe way, and there is a casual way that could cause problems.

To explain the alternatives I'll construct a simple scenario, I'll call this the survey problem.  Simply stated you would like to offer a Survey that your contacts can fill out, once and only once and no other public users should be allowed to fill in this survey. 

You can easily and quickly build such a survey using Visualforce, and using Force.com Sites you can make this available to the contacts in your system.  In order to do this you must send them the URL of the survey that they can then visit and fill in.   If you send everyone the same URL, you'll never know which data came from which account / customer, so you construct a URL that contains specific information that allows them to visit this survey and complete the data, storing the resulting info in a record, let's say a custom object called Survey Results.

So far so good, however if you have sent the URL with a valid Force.com custom object Record Identifier ( casual way ) then you will open up your database to potential trouble.  Since the identifier will be used by the Visualforce page, first, the record identifier could be used twice, and the user could guess the identifiers that will allow them to access other records in your system using the Visualforce page that you provided.

The secure solution is to generate a KEY that is specific to something in the system, in the case described this is unique to one contact and one survey.   I have built a simple Survey app that demonstrates this, which I intend to publish in a few weeks.  For today, I'd like to share the method that I used to generate a unique KEY that is not easily guessed.   A user who approaches your Visualforce page with a valid KEY can then be given access to input or edit data, and a user who has hacked a KEY will not.

The KEY is generated by the system using a trigger when you construct an invitation to take the survey, this is a custom object also.  The KEY is both stored in the system and also sent out to the contact in the URL that you provide.  When your contact returns using the URL to your Site, the Visualforce page can match this KEY to one in your system and thus verify that it's valid (because it matches one that you have stored), therefore you can safely allow them to input / update data.

How to build a KEY ?   It's really a simple process of grabing a time-stamp, adding a bit of unique data and then running that string through MD5 to create a digest.  The key is finally url encoded to allow it to be sent as a link in an outbound email.  Here is the code for my custom object trigger, this results in the KEY stored in the  Invite_Code__c field on the Invitation__c custom object.

trigger invite_code on Invitation__c (before insert) {
    for (Invitation__c i : Trigger.new) {
        DateTime now = System.now();        
        String formattednow = now.formatGmt('yyyy-MM-dd')+'T'+ now.formatGmt('HH:mm:ss')+'.'+now.formatGMT('SSS')+'Z';        
        String canonical = i.id + i.Name + formattednow;                       
        Blob bsig = Crypto.generateDigest('MD5', Blob.valueOf(canonical));        
        String token =  EncodingUtil.base64Encode(bsig);                
        if(token.length() > 255) { token =  token.substring(0,254); }       
        i.Invite_Code__c = Encodingutil.urlEncode(token, 'UTF-8').replaceAll('%','_');
    }
}

The tokens or KEY that are generated look like this : N2oruzX_2FNWKEytb1yy6oSA_3D_3D
In order to defeat this method, you are faced with an impossible problem, even if you know how the key is constructed, it's not possible to construct a valid key since an attacker cannot insert the key into your system ahead of time, and if they guess the time stamp correctly they are forced to guess the record ID and the name of the valid record as well.

You can easily modify the source code at the line that constructs the canonical variable to append additional information that is unique to your application and therefore have a unique algorithm that is not knowable by anyone reading this article.

Stay tuned to this blog, I'll be posting additional tips and tricks as I build a simple app that demonstrates the power of Force.com Sites using Visualforce to gather information from your customer.

Published
April 13, 2009

Leave your comments...

Best Practice: Sites and record identifiers