Your current location is › Premium Web Directory » Article Details
  
   

Latest Featured Listings
• Golden Recipes
• Mortgage Advisor
• SEO: Search Engine Optimisation - Internet ...

Top Hits
• The Jaguar Enthusiast's Club
• Dadaf.com Web Directory
• Critical Illness, Life Insurance and Incom ...

Latest Additions
• Golden Recipes
• ShareRight Documentary
• Canadian Wholesalers & Dropshippers Di ...

Directory Statistics
• Total Categories: 3,713
• Active Listings: 52
• Pending Listings: 6
• Listings Added Today: 0

Securing Data Sent Via GET Requests

Add to: Technorati
Add to: Del.ico.us
Add to: StumbleUpon
Date Added: June 10, 2007 04:20:07 AM
Author: Hillel Aftel

This article is intended for users with an intermediate to advanced knowledge of PHP, HTML, and GET requests. 

In this article I'm going to show you how you can use PHP to encode your data for transit. Most importantly, it will be done in a way that makes the data decodable, and therefore much more usable, by the receiving page. 

If you're reading this article, you know that GET requests are a common method for transmitting data from one web page to another. GET requests are the method of choice in cases where the data to be transmitted is relatively short, since they allow you to send data simply by writing it into a URL. The problem with this method is that it transmits data in a completely insecure way, since it usually requires displaying data directly in the address bar of the client browser. 

You may already be familiar with the many encoding functions PHP has to offer. These generally consist of hashing functions, such as sha1(), md5(), and crc32(). While these functions will encode data so that no human can read it, they also ensure that the page accepting the data transmission won't be able to read it either. This may be fine for storing data that will be needed later for comparison-only, such as passwords, however it's useless if you need to know what the data actually represents. Contact information, dates, user preferences, confidential client information, etc, all present this problem. 

Key Advantages of this Encryption Method

 1. Unlike some other methods, this one doesn't require sending a separate "key" along with the encoded data, to tell the decoder how to decode. Using this method, everything the decoder needs, including the key and the data itself, is sent within a single string.

2. This script will not use the same encryption scheme every time it's used. For example, if you encode the same word twice, there is only a 1 in 10 chance that the encoded string will look the same each time. This adds an element of randomness that makes the encryption harder to crack. 

How it Works

I'll first outline how the encoding/decoding scheme works in plain English (as plain as the subject matter allows for), and then I'll take you through the PHP code. If you'd like to see the finished script now, you can download it here. The script consists of two functions: an encoder and a decoder. 

With regard to encoding, there are many different ways to go about it. One way is to perform a series of mathematical operations on the data. However that method carries several disadvantages, or at least, several complications: such as unpredictable output length, and the difficulty of working mathematically with non-numeric ("string") data. 

The method we'll be using will define an array that contains static value pairs. Each pair will consist of a decoded value and an encoded value. This essentially makes the array into a simple reference table. In order to support the full range of ASCII characters, the values will be in terms of numeric ASCII codes. For example:
$anArray[123] = 54;
So in order to encode or decode a character, the function simply needs to look up that character's ASCII value in the array, rather than perform a series of calculations.

The array will have as many elements as there are screen-printable characters. This consists of ASCII codes 32 through 126 — the basic printable character set, which includes the space character as well as typable symbols. 

The script is divided into 3 separate PHP files:

The first file contains the bulk of the script. It's where we declare the functions.

 
The second is a "single-use" script that only needs to be executed once. Its purpose is to generate the third file.

 The third file will serve as an include file for our encode/decode functions. It defines the static array (our reference table). The array is produced by randomly shuffling the ASCII codes in our range, so that, for example, the character code 56 corresponds to a different random character code, like 121. The purpose of generating this file yourself (rather than using a standard distributed static file) is to perform a new shuffling of ASCII codes for yourself — making the chances of anyone else using the same encoding scheme virtually nil.

A Note on ASCII Ranges

You can customize the ASCII range that this script supports. Simply change the arguments passed to range(), in the second line of mine.php.

It is recommended that you limit the ASCII range to include only the characters that you need to support in your specific scenario. This is because any characters within that range will be possible choices for an encoded replacement; meaning that even if you never use them, they can all become part of the encoded string. While this is not necessarily a bad thing, most characters outside the basic ASCII set will need to be URL-encoded, and that will produce a longer encoded string. In the interest of minimizing the length of transmitted data, you should limit the range to cover only those characters you foresee needing.

In the vast majority of cases, GET requests are only used to transmit characters from the basic ASCII set. Using only the basic ASCII set, as opposed to, say, the basic + extended sets, will not only cut down on the average transmission length, but also cut the reference array size in half. The basic ASCII set still covers the space character as well as all typable symbols.

Conversely, you can extend the power of this script by extending the range to cover the entire ASCII table. This will allow you to encode things like file contents, emails, or multi-line form submissions. The full ASCII range includes line separators, tabs, and other non-printing characters, so formatting would be preserved in those instances.

ASCII Range Reference
  • Basic: 32 - 126
  • Basic + Extended: 32 - 255
  • All including non-printing characters: 0 - 255
We'll further secure the system by defining 10 different complete sets of ASCII value pairs, in a multi-dimensional array, to be chosen from at random at each call to the encode function. This way, the same data encoded twice, even by the same user, will look completely different 9 out of 10 times — making the encoded string all the more confusing to look at.

In order to tell the decoder function which data set was used, we will "tag" our encoded data with a character placed at a specific position within the encoded string. When the decoder reads the character at that position, it will be able to determine which data set it needs to use to decode the data. We'll also pad the left and right sides of the encoded string with random dummy digits, just to add some additional confusion. 

The decoder function will basically do everything the encoder did, but in reverse. It will pick apart the encoded string, stripping away the characters at positions that are known to always contain useless data, while reading the characters at positions known to contain actual data. It will then use the reference array to find the values that correspond to those in the encoded string, and convert each one back to its original value — thus reforming the original string.

 
On to the Code

 The first thing we'll do is write the single-use script that generates the include file. I call this file mine.php (because it produces salt.php), but you can choose any name you like, as this page is only run manually by you. It never gets called by any of the other code.

$saltfile = 'salt.php'; 
This defines the name of the file to create. You should choose a unique name, to prevent the possibility of someone else somehow including the file in their own script and cracking the encoding scheme. Once you change the filename here, you also need to change the name used in the include() statements. There is one include() statement in each function; be sure to change them both, or else this won't work.
$ascii = range(32,126); 
This creates an array of values ranging from 32 through 126, which is our ASCII range. You can change the arguments here to suit your own ASCII range requirements (see sidebar).
$ascii = array_combine($ascii, $ascii);
We need the key names to also be ASCII codes, so we use array_combine, and tell it to get both the key names and values from the same array. What we now have in $ascii is an array that consists of identical key/value pairs. $ascii[32] == 32, $ascii[79] == 79, and so on.
$handle = fopen($saltfile, 'x');
This opens our file for writing. The 'x' argument will cause the file to be created if it doesn't already exist, and throw a PHP warning if the file does exist.

Note:

For added security, you can opt to allow periodic regeneration of the salt file by changing the 'x' argument to a 'w'. This will allow overwriting of any pre-existing salt file.

I specify the 'x' argument here by default because accidental overwrites of the salt file while the encode/decode functions are in live use can be hazardous. Any data that's in-transit at the time of the overwrite will end up garbled after being decoded. If you decide to specify the 'w' argument, I recommend moving mine.php to a separate directory. That will allow you to run this script as much as you want without having to worry about accidentally overwriting the in-use salt file.

/* The file we're creating will need to be a PHP include file, so we need to wrap its contents in PHP start/end tags. In this write operation we also start declaring the array, which I've chosen to call $salt. */
fwrite($handle, "<?n".'$salt=array('."n");

// Since we want to output 10 different sets of data, we begin a for() loop that has 10 steps.
for ($i=0;$i<10; $i++){

//Here we save the present state of the $ascii array, because we'll be using shuffle() on it, which will permanenetly change it.
$rawAscii = $ascii;

// Shuffle the array values.
shuffle($ascii);

/* shuffle() erases the key names from the array, so we need to assign them again by using array_combine().
We get the key names from our original saved $ascii array. */

$newAscii = array_combine($ascii, $rawAscii);

// In our next write operation, we define the first dimension of the $salt array (with key names 0 through 9, determined by $i).
fwrite($handle, $i."=>array(n");

/* Now we loop through the $newAscii array, writing code to the page that will define the bulk of the array.
We need to start the loop at 32 and also add 32 to the max loop iterations, because our array's key names
start at 32. The rest is fairly self-explanatory. */

for ($x=32;$x<count($newAscii)+32; $x++){
fwrite($handle, $x.'=>'.$newAscii[$x]);
if ($x<count($newAscii)+31){fwrite($handle, ",");}
}
fwrite($handle, ")");
if ($i<9){fwrite($handle, ",");}
fwrite($handle, "n");
}
fwrite($handle, ");n?>");
fclose($handle);
echo 'Done.';
Upon running the code above, a PHP file will be output, containing a single large two-dimensional array definition.

Now we can start writing the encoder/decoder functions. I call the encoder ob(), short for "obfuscate," and the decoder deob(), short for, you guessed it, "deobfuscate".

 

First, we'll need a source of pseudorandom data. We'll use this data to make a choice from one of the 10 data sets in $salt, as well as to provide some of the random "dummy" digits we'll use to pad the encoded string. I use microtime() for this, but there are many different ways to get pseudorandom data in PHP. microtime() gets the UNIX timestamp down to the microsecond, so most of its digits are sufficiently random for our purposes.
function ob($in){

// Before we do anything, include the salt file.
include('salt.php');

// Get the current microtime.
$m = microtime();

// Take various digits from microtime to use as dummy characters.
$t = substr($m,3,1);
$v = substr($m,5,3);

// Take a digit to serve as our choice from the $salt array.
$which = substr($m,2,1);

// Loop though the characters that make up the input string.
for ($i=0; $i < strlen($in); $i++){

// Get the ASCII code of the character using ord().
$ascii = ord(substr($in,$i,1));

// Get the corresponding replacement ASCII code from our chosen key of the $salt array and append it to a variable,
thereby contructing the encoded version of the input string.
$newIn .= chr($salt[$which][$ascii]);
}

/* Finally, stitch together and return the complete encoded string. $v and $t are random dummy digits. $which is the
digit that tells the decoder which data set was used to encode this string. We send this along with the actual
encoded data; thus the advantage of not having to send along a separate decode key, as in other encryption methods.
We also need to make sure our output string is transmittable via URL, which we do using urlencode(). */

return urlencode($v.$newIn.$which.$t);
}
Here's the decoder function, which simply reverses all the changes made by the encoder:
function deob($in){
include('salt.php');

// Since the encoder used urlencode() on the encoded string, we need to first urldecode() it.
$in = urldecode($in);

// Read the character that tells us which data set was used from $salt to encode the string.
$which = substr($in,-2,1);

// From this point on we only want to work with the section of the string that contains the actual data that was encoded.
$in = substr($in,3,-2);

// Loop through each character in the encoded data.
for ($i=0; $i < strlen($in); $i++){

// Get the ASCII code for the character.
$ascii = ord(substr($in,$i,1));

// Search the $salt array values for the ASCII code, convert that value's key name (the ASCII code for the original
un-encoded character) to a character, and append it to a variable, thereby reconstructing the original string.
$newIn .= chr(array_search($ascii,$salt[$which]));
}
return $newIn;
}

Usage

 First, generate the reference array file by simply navigating to mine.php from a web browser. The new file should then appear in the same directory where mine.php resides. Note that this step only needs to be done once.

Then, to call the encoding and decoding functions from your own script, first include the main script file:
include('obfuscate.php');
Use ob() to encode data before sending it. ob() takes a single string argument, which can be a variable or a string literal:
$queryValue = ob($value);

// or,

$queryValue = ob('some query data');
Spaces are acceptable in the argument, and will be preserved in the decoded output. On the receiving page, decode the query string with deob(). deob() also takes a single string argument:
$value = deob($queryValue);
You're Done
You now have the tools you need to take full advantage of the convenience of GET requests, without ever needing to display insecure data in the client browser.