Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

query string parameter obfuscation

I want to obfuscate one query string parameter in ASP.NET. The site will have a high volume of request, so the algorithm shouldn't be too slow.

My problem is that all the algorithms I found result in unwanted characters (like +/=)

Here is an example of what i want to achieve:

www.domain.com/?id=1844

to

www.domain.com/?id=3GQ5DTL3oVd91WsGj74gcQ

The obfuscated param should only include a-z and A-Z and 0-9 characters.

I know I can encrypt using base64, but this will generate unwanted characters such as / or = or +.

Any idea what algorithm can be used?

Update: I'm aware of UrlEncoding , i want to avoid encoding the string. because that will generate charaters like %F2 or %B2 in the url.

like image 896
RuSh Avatar asked Aug 25 '10 20:08

RuSh


1 Answers

You can use triple DES to encode the value using a narow block cipher.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace ConsoleApplication1 {
    class Program {
        static string ToHex(byte[] value) {
            StringBuilder sb = new StringBuilder();
            foreach (byte b in value)
                sb.AppendFormat("{0:x2}", b);
            return sb.ToString();
        }
        static string Encode(long value, byte[] key) {
            byte[] InputBuffer = new byte[8];
            byte[] OutputBuffer;
            unsafe {
                fixed (byte* pInputBuffer = InputBuffer) {
                    ((long*)pInputBuffer)[0] = value;
                }
            }
            TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider();
            TDes.Mode = CipherMode.ECB;
            TDes.Padding = PaddingMode.None;
            TDes.Key = key;

            using (ICryptoTransform Encryptor = TDes.CreateEncryptor()) {
                OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 8);
            }
            TDes.Clear();

            return ToHex(OutputBuffer);
        }
        static long Decode(string value, byte[] key) {
            byte[] InputBuffer = new byte[8];
            byte[] OutputBuffer;

            for (int i = 0; i < 8; i++) {
                InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
            }

            TripleDESCryptoServiceProvider TDes = new TripleDESCryptoServiceProvider();
            TDes.Mode = CipherMode.ECB;
            TDes.Padding = PaddingMode.None;
            TDes.Key = key;

            using (ICryptoTransform Decryptor = TDes.CreateDecryptor()) {
                OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 8);
            }
            TDes.Clear();

            unsafe {
                fixed (byte* pOutputBuffer = OutputBuffer) {
                    return ((long*)pOutputBuffer)[0];
                }
            }
        }
        static void Main(string[] args) {
            long NumberToEncode = (new Random()).Next();
            Console.WriteLine("Number to encode = {0}.", NumberToEncode);
            byte[] Key = new byte[24];
            (new RNGCryptoServiceProvider()).GetBytes(Key);
            Console.WriteLine("Key to encode with is {0}.", ToHex(Key));
            string EncodedValue = Encode(NumberToEncode, Key);
            Console.WriteLine("The encoded value is {0}.", EncodedValue);
            long DecodedValue = Decode(EncodedValue, Key);
            Console.WriteLine("The decoded result is {0}.", DecodedValue);
        }
    }
}

The output should be something like this:

Number to encode = 873435734.
Key to encode with is 38137b6a7aa49cc6040c4297064fdb4461c79a895f40b4d1.
The encoded value is 43ba3fb809a47b2f.
The decoded result is 873435734.

Note that the encoded value is only 16 characters wide.

If you're really conserned about abuse, then AES can be used in a similar manner. In the next example I switch in AES and write the 64 bit id number into both sides of the block. If it doesn't decode with the same value on both sides then it is rejected. This can prevent people from writing in random numbers.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;

namespace ConsoleApplication1 {
    class Program {
        static string ToHex(byte[] value) {
            StringBuilder sb = new StringBuilder();
            foreach (byte b in value)
                sb.AppendFormat("{0:x2}", b);
            return sb.ToString();
        }
        static string Encode(long value, byte[] key) {
            byte[] InputBuffer = new byte[16];
            byte[] OutputBuffer;
            unsafe {
                fixed (byte* pInputBuffer = InputBuffer) {
                    ((long*)pInputBuffer)[0] = value;
                    ((long*)pInputBuffer)[1] = value;
                }
            }
            AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
            Aes.Mode = CipherMode.ECB;
            Aes.Padding = PaddingMode.None;
            Aes.Key = key;

            using (ICryptoTransform Encryptor = Aes.CreateEncryptor()) {
                OutputBuffer = Encryptor.TransformFinalBlock(InputBuffer, 0, 16);
            }
            Aes.Clear();

            return ToHex(OutputBuffer);
        }
        static bool TryDecode(string value, byte[] key, out long result) {
            byte[] InputBuffer = new byte[16];
            byte[] OutputBuffer;

            for (int i = 0; i < 16; i++) {
                InputBuffer[i] = Convert.ToByte(value.Substring(i * 2, 2), 16);
            }

            AesCryptoServiceProvider Aes = new AesCryptoServiceProvider();
            Aes.Mode = CipherMode.ECB;
            Aes.Padding = PaddingMode.None;
            Aes.Key = key;

            using (ICryptoTransform Decryptor = Aes.CreateDecryptor()) {
                OutputBuffer = Decryptor.TransformFinalBlock(InputBuffer, 0, 16);
            }
            Aes.Clear();

            unsafe {
                fixed (byte* pOutputBuffer = OutputBuffer) {
                    //return ((long*)pOutputBuffer)[0];
                    if (((long*)pOutputBuffer)[0] == ((long*)pOutputBuffer)[1]) {
                        result = ((long*)pOutputBuffer)[0];
                        return true;
                    }
                    else {
                        result = 0;
                        return false;
                    }
                }
            }
        }
        static void Main(string[] args) {
            long NumberToEncode = (new Random()).Next();
            Console.WriteLine("Number to encode = {0}.", NumberToEncode);
            byte[] Key = new byte[24];
            (new RNGCryptoServiceProvider()).GetBytes(Key);
            Console.WriteLine("Key to encode with is {0}.", ToHex(Key));
            string EncodedValue = Encode(NumberToEncode, Key);
            Console.WriteLine("The encoded value is {0}.", EncodedValue);
            long DecodedValue;
            bool Success = TryDecode(EncodedValue, Key, out DecodedValue);
            if (Success) {
                Console.WriteLine("Successfully decoded the encoded value.");
                Console.WriteLine("The decoded result is {0}.", DecodedValue);
            }
            else
                Console.WriteLine("Failed to decode encoded value. Invalid result.");
        }
    }
}

The result should now look something like this:

Number to encode = 1795789891.
Key to encode with is 6c90323644c841a00d40d4407e23dbb2ab56530e1a4bae43.
The encoded value is 731fceec2af2fcc2790883f2b79e9a01.
Successfully decoded the encoded value.
The decoded result is 1795789891.

Also note that since we have now used a wider block cipher the encoded value is now 32 characters wide.

like image 59
Lunatic Experimentalist Avatar answered Sep 19 '22 13:09

Lunatic Experimentalist