Click here to Skip to main content
15,901,666 members
Articles / Web Development / ASP.NET
Article

Encrypt a Querystring with Expiration

Rate me:
Please Sign up or sign in to vote.
4.16/5 (6 votes)
22 Aug 2007CPOL2 min read 67.8K   575   35   11
Create a querystring that is encrypted and expires

Introduction

This is an enhancement and "mash" to several articles on encryption and changing passwords. The problem is that I needed something that is not only encrypted, but also easy to implement as well as something that expires.

Background

Several months ago, I implemented an application using the ASP.NET membership controls which are in Visual Studio 2005. Part of this implementation allows for password recovery. The problem that I was faced with is the password that goes back to the user is not only very strange but the whole process is a little cumbersome. After looking around on the Internet, I came across a way to reset the password based on pushing a link from an email. The problem with this approach is that if someone does not delete the email after using it or if someone can interpret the userid then the password could be reset without someone's permission or knowledge. That is why I created a small class that has solved these problems. In essence, it takes an encryption key along with the username and a time stamp and creates a URL with the encrypted key.

The decryption part takes the URL and using the encryption key verifies that the date and time stamp is still valid. If it is, then you can assume that this is the user within a period of a couple of minutes. I recommend setting the length to no more than 30 minutes, but it is configurable.

Inside this file, I have also attached a small sample application which shows how to implement this. Once you get the hang of it, you can use it for just about anything. If there is a better way or comments, then please let me know.

Using the Code

Download the code and open up the encryption class. It contains three properties and two public functions.

The properties are:

VB.NET
//
Public Property UserName() As String
    Get
        Return m_username
    End Get
    Set(ByVal value As String)
        m_username = value
    End Set
End Property

Public Property EncryptionKey() As String
    Get
        Return m_EncryptionKey
    End Get
    Set(ByVal value As String)
       m_EncryptionKey = value
    End Set
End Property
Public Property ExpireDateTime() As DateTime
    Get
       Return m_ExpireDateTime
    End Get
    Set(ByVal value As DateTime)
       m_ExpireDateTime = value
    End Set
End Property
//

The two public class are:

VB.NET
Public Function Encrypt() As String
    If String.IsNullOrEmpty(m_EncryptionKey) Then
        Throw New Exception("Encryption Key is Required")
    End If

    'Ensure that timeStampKey exists and update the expiration time.
    If String.IsNullOrEmpty(m_ExpireDateTime.ToString) Then
        Throw New Exception("Expiration Date is Required")
    End If
    Dim buffer() As Byte = Encoding.ASCII.GetBytes(serialize())
    Dim des As TripleDESCryptoServiceProvider = New TripleDESCryptoServiceProvider
    Dim MD5 As MD5CryptoServiceProvider = New MD5CryptoServiceProvider
    des.Key = MD5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(m_EncryptionKey))
    des.IV = IV
    Return Convert.ToBase64String(des.CreateEncryptor.TransformFinalBlock_
            (buffer, 0, buffer.Length))
End Function

Public Function Decrypt(ByVal EncryptedUsername As String) As String
    If String.IsNullOrEmpty(m_EncryptionKey) Then
        Throw New Exception("Encryption Key is not set")
    End If
    Try
        Dim buffer() As Byte = Convert.FromBase64String(EncryptedUsername)
        Dim des As TripleDESCryptoServiceProvider = New TripleDESCryptoServiceProvider
        Dim MD5 As MD5CryptoServiceProvider = New MD5CryptoServiceProvider
        des.Key = MD5.ComputeHash(ASCIIEncoding.ASCII.GetBytes(m_EncryptionKey))
        des.IV = IV
        deserialize(Encoding.ASCII.GetString_
            (des.CreateDecryptor.TransformFinalBlock(buffer, 0, buffer.Length)))
        Return Encoding.ASCII.GetString(des.CreateDecryptor.TransformFinalBlock_
                (buffer, 0, buffer.Length))
   Catch ex As CryptographicException
        Throw New Exception("Crypto error")
   Catch ex As FormatException
        Throw New Exception("FormatExpection")
   End Try
End Function

Points of Interest

This code was taken from several different articles on this site and combined to create this class. These articles are as follows: Password Recovery and TamperProofQueryString along with some original code.

History

  • 08.22.2007 - Version 1 (Initial upload)

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
United States United States
I have worked for Fortune 500 company developing .NET, SAP and Oracle applications. I left the company to spend more time with my family and now work for a local government in Virginia developing web applications for them which is about 10 minutes from home.

I enjoy programming but enjoy my family even more.

Comments and Discussions

 
GeneralPlease publish a VS2008 version Pin
Tatworth12-Aug-09 0:48
Tatworth12-Aug-09 0:48 
GeneralEncrypt Querystring Pin
IrishChieftain10-Sep-07 15:37
IrishChieftain10-Sep-07 15:37 
GeneralRe: Encrypt Querystring Pin
David Bauernschmidt10-Sep-07 16:26
David Bauernschmidt10-Sep-07 16:26 
GeneralRe: Encrypt Querystring Pin
tengtium15-Nov-09 0:27
tengtium15-Nov-09 0:27 
GeneralInteresting Idea to a Common Problem Pin
merlin98123-Aug-07 6:43
professionalmerlin98123-Aug-07 6:43 
QuestionWhy? Pin
Boyan K22-Aug-07 16:22
Boyan K22-Aug-07 16:22 
AnswerRe: Why? Pin
David Bauernschmidt22-Aug-07 17:47
David Bauernschmidt22-Aug-07 17:47 
AnswerRe: Why? Pin
Edward Steward25-Aug-07 2:38
Edward Steward25-Aug-07 2:38 
GeneralRe: Why? Pin
cambo198227-Aug-07 23:50
cambo198227-Aug-07 23:50 
Agreed. I don't doubt that this is a good demonstration of the concept. Theres probably other places that it could be used like "Verify my account" when you sign up to a service etc...
But I think you're over complicating the scenario for your password reset/recovery.

Lets take 2 steps back for a minute... what do you mean by password "recovery". I hope that you're not actually able to recover a persons password from your DB. Ideally you should be storing some sort of MD5/Hash of the users password which is essentially unrecoverable if they lose it and not storing peoples passwords in plain text. So this scenario should be for a password reset.

To allow them to reset their own password, the scenario outlined in Edward Stewarts reply makes more sense... (with the exception that you can't ask them to enter their username/original password if they've forgotten their original password which is kind of the point of this system)

1. A person requests a password reset for a username/userid

2. You generate a record in the "PasswordResetRequest" table containing the following info
GUID,
userid, //the account ID being reset
timestamp, //the expiry time for this reset attempt

3. You email them a URL something like
https://www.mysite.com/reset.aspx?resetID=<guid>

4. When they hit that URL, you check the database for the GUID and if it exists, you check the timestamp to see if the attempt is still within a valid timeframe... if it is, you allow them to change their password... and once they do, you delete the record
(or mark it with a flag to say its been used and can't be used again)

5. That method is quite secure and safe, (especially if your reset page is under SSL) and saves you alot of hassle with encrypting everything into Querystrings...

The only way someone could breach that would be
a. They guess the GUID, 2^128 is pretty unlikely Wink | ;)

b. They intercept the email before the user gets to it... and theres not much you can do about that... both the scenario I outlined and your scenario where you mail them the encrypted url are susceptible to this... but then if you require that kind of secure communication there PGP/GPG type security that can be applied to email though I doubt its applicable here.



Don't get me wrong, its a good setup though and like I said I'm sure it has applications outside of Password Resets.

A few suggestions though,
1. Your encryption key & init vector are hardcoded into your code... so you've put all your "goods" in one place and if your server were ever breached... I now have your key/IV and I can generate valid URLs to use on your website... It might be an idea to move these to the Web.Config, and then encrypt your web.config file. Have a look here

http://blogs.msdn.com/pedros_spot/archive/2005/11/17/494022.aspx[^]

2. As someone else pointed out, be careful with your try/catch/finallys
In your "Decrypt" method, you catch some different exceptions but then lose all the helpful information and stacktrace data they contain. If you really need to wrap up your exceptions like that, you should probably create your own ApplicationException extended class and always pass the caught exception as a parameter... (sorry I'm a C# person Smile | :) )

try<br />
{<br />
   //Do Some Stuff<br />
}<br />
catch CryptographicException ex<br />
{<br />
   throw new MyCustomApplicationException("My Custom Crypto Exception Message", ex);<br />
}





Rgds,
~cambo
AnswerRe: Why? Pin
David Bauernschmidt28-Aug-07 16:03
David Bauernschmidt28-Aug-07 16:03 
GeneralPlease don't do this Pin
Ennis Ray Lynch, Jr.22-Aug-07 12:34
Ennis Ray Lynch, Jr.22-Aug-07 12:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.