Bob.at.Indigo.Health
Bob.at.Indigo.Health

Reputation: 11895

Encrypt user's record index

My ASP.NET MVC web application creates some data on behalf of a user and stores it in a SQL database in a record with a numeric primary key. I access the data via the primary key.

I want my authenticated users to be able to navigate to a URL that contains the (numeric) primary key, but I don't want to expose the actual key value. So, it seems to me that I would want to encrypt the numeric key (using a symmetrical encryption algorithm) with a password consisting of a string baked into my code plus the logged-in user's UserID. The resultant URL would look something like: https://foo.com/123abc, where "123abc" is the encrypted key value (converted from bytes to characters). In theory (to my beginner brain) this encrypted value, even if acquired by a malicious party, would not be useful unless that party could log in to my website using the user's credentials.

Question 1: Is this the correct way to do this sort of thing, and
Question 2: Can someone who knows this stuff point me at a simple symmetrical encryption API that I can use for this purpose.

Upvotes: 0

Views: 412

Answers (3)

RandomUs1r
RandomUs1r

Reputation: 4190

Rather than use the PK, you can add a column to your SQL table and setting its type to uniqueidentifier and it's value to NEWID() and then display that to the user, this solution would have the least amount of overhead, while still providing a seemingly random series that you can tie back to that user later.

ALTER TABLE foo ADD foobar uniqueIdentifier default newid();

http://msdn.microsoft.com/en-us/library/ms187942.aspx

Upvotes: 2

Chris Pratt
Chris Pratt

Reputation: 239290

Symmetrical encryption of an integer would be so ridiculously easy to crack you might as well not even bother. Now, you could salt it or obfuscate it a bit by Base64 encoding it or something like that, but still, this is pretty pointless. A database primary key is not sensitive data. It's meaningless without access to the database, and if they have access to the database, looking up a particular user by their id is the absolutely least of your problems. Even symmetrical encryption is going to add significant overhead to your application for something that's simply not necessary.

If you really don't want the PK exposed, then use something else like the username in the URL and look up the user by that.

Upvotes: 1

Sten Petrov
Sten Petrov

Reputation: 11040

You can do that, sure.

Store the salt in your session, generate one randomly every time or use the session ID as salt.

Below are two methods that could encrypt/decrypt your string value with salt. You can use the salt in place of initial vector.

public static string Encrypt(string PlainText, string Password, string Salt,
 string HashAlgorithm = "SHA1", int PasswordIterations = 16, string InitialVector = "Initial Vector", int KeySize = 256)
{  
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
    byte[] PlainTextBytes = Encoding.UTF8.GetBytes(PlainText);
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.CBC;

    ICryptoTransform Encryptor = SymmetricKey.CreateEncryptor(KeyBytes, InitialVectorBytes);

    MemoryStream MemStream = new MemoryStream();
    CryptoStream cryptoStream = new CryptoStream(MemStream, Encryptor, CryptoStreamMode.Write);
    cryptoStream.Write(PlainTextBytes, 0, PlainTextBytes.Length);
    cryptoStream.FlushFinalBlock();
    byte[] CipherTextBytes = MemStream.ToArray();

    MemStream.Close();
    cryptoStream.Close();
    Encryptor.Dispose();
    Encryptor = null;

    return Convert.ToBase64String(CipherTextBytes);
}


public static string Decrypt(string CipherText, string Password, string Salt,
    string HashAlgorithm = "SHA1", int PasswordIterations = 16, string InitialVector = "Initial Vector", int KeySize = 256)
{ 
    byte[] InitialVectorBytes = Encoding.ASCII.GetBytes(InitialVector);
    byte[] SaltValueBytes = Encoding.ASCII.GetBytes(Salt);
    byte[] CipherTextBytes = Convert.FromBase64String(CipherText);
    PasswordDeriveBytes DerivedPassword = new PasswordDeriveBytes(Password, SaltValueBytes, HashAlgorithm, PasswordIterations);
    byte[] KeyBytes = DerivedPassword.GetBytes(KeySize / 8);
    RijndaelManaged SymmetricKey = new RijndaelManaged();
    SymmetricKey.Mode = CipherMode.CBC;

    ICryptoTransform Decryptor = SymmetricKey.CreateDecryptor(KeyBytes, InitialVectorBytes);
    MemoryStream MemStream = new MemoryStream(CipherTextBytes);
    CryptoStream cryptoStream = new CryptoStream(MemStream, Decryptor, CryptoStreamMode.Read);
    byte[] PlainTextBytes = new byte[CipherTextBytes.Length];

    int ByteCount = cryptoStream.Read(PlainTextBytes, 0, PlainTextBytes.Length);

    MemStream.Close();
    cryptoStream.Close();
    Decryptor.Dispose();
    Decryptor = null;

    return Encoding.UTF8.GetString(PlainTextBytes, 0, ByteCount);
}

Upvotes: 0

Related Questions