homepage Welcome to WebmasterWorld Guest from 54.196.77.82
register, free tools, login, search, pro membership, help, library, announcements, recent posts, open posts,
Become a Pro Member

Visit PubCon.com
Home / Forums Index / Microsoft / Microsoft IIS Web Server and ASP.NET
Forum Library, Charter, Moderators: ocean10000

Microsoft IIS Web Server and ASP.NET Forum

    
Simple two-factor authentication using ASP.NET
Extra application security by using SMS tokens
marcel




msg:4189652
 9:08 am on Aug 20, 2010 (gmt 0)

Recently a project I am working on decided to adapt the Intranet version of a Web Application to an Internet version. Amongst other things we changed the standard login system from Windows to Forms authentication and supplied the website with a Security certificate and all was fine, until a few customers decided that standard password authentication wouldn't cut it.

After looking at a few existing solutions (SecurId, Hardware SMS systems etc.) and being unpleasantly surprised by their pricing, we decided to build a simple system ourselves.

Basically we wanted a system that works as follows:
* User goes to website and is confronted with a login screen.
* User enters their standard login/password combination and submits.
* A Code (Token) is generated and sent to the User by SMS.
* User enters the token, if it matches they are logged in.

To create this example we need the following:
* .NET Membership [msdn.microsoft.com]
* A Database, .NET User Profiles [msdn.microsoft.com] or other method for storing user mobile phone numbers. For this example we will use a fixed number.
* An SMS gateway provider (for this example I will use VoipBuster)

First we will create a simple form with two PlaceHolders; One containing the Login area, and the other hidden placeholder containing a 'Token' entry form:

Page source
<asp:PlaceHolder ID="plcLoginArea" runat="server">
User name: <asp:TextBox ID="txtUserName" runat="server" />
<br />
Password: <asp:TextBox ID="txtPassword" runat="server" TextMode="Password" />
<br />
<asp:Button ID="btnSubmitLogin" runat="server" Text="Log in" OnClick="btnSubmitLogin_Click" />
</asp:PlaceHolder>

<asp:PlaceHolder ID="plcTokenEntry" runat="server" Visible="false">
Enter Token: <asp:TextBox ID="txtToken" runat="server" />
<br />
<asp:Button ID="btnTokenEntry" runat="server" Text="Submit" OnClick="btnTokenEntry_Click" />
</asp:PlaceHolder>


Nothing special, no pretty markup or any attempt to validate input, I want to keep this simple.

Helper Classes

We need two simple Helper Classes, one to generate a token, and the second one to send the Text Message:

Generate Token (VB)
Private Function GenerateToken(ByVal length As Integer) As String
Dim sb As New StringBuilder()
' Wait to force new Seed
System.Threading.Thread.Sleep(20)
Dim rnd As New Random()
Dim ch As Char
Dim i As Integer
For i = 1 To length
ch = Convert.ToChar(Convert.ToInt32(25 * rnd.NextDouble() + 65))
sb.Append(ch)
Next i
Return sb.ToString()
End Function


Generate Token (C#)
private string GenerateToken(int length)
{
System.Text.StringBuilder sb = new System.Text.StringBuilder();
// Wait to force new Seed
System.Threading.Thread.Sleep(20);
Random rnd = new Random();
char ch;
int i = 0;
for (i = 1; i <= length; i++)
{
ch = Convert.ToChar(Convert.ToInt32(25 * rnd.NextDouble() + 65));
sb.Append(ch);
}
return sb.ToString();
}


Send SMS (VB)
Private Function SendSMS(ByVal token As String, ByVal number As String) As Boolean
Dim URL As String = "https://www.voipbuster.com/myaccount/sendsms.php?username={0}&password={1}&from={2}&to={3}&text={4}"
URL = String.Format(URL, "userName", "password", "0123456789", number, token)
Try
Dim wRequest As Net.WebRequest = Net.WebRequest.Create(URL)
wRequest.GetResponse()
Return True
Catch ex As Exception
Return False
End Try
End Function


Send SMS (C#)
private bool SendSMS(string token, string number)
{
string URL = "https://www.voipbuster.com/myaccount/sendsms.php?username={0}&password={1}&from={2}&to={3}&text={4}";
URL = string.Format(URL, "userName", "password", "0123456789", number, token);
try
{
System.Net.WebRequest wRequest = System.Net.WebRequest.Create(URL);
wRequest.GetResponse();
return true;
}
catch (Exception ex)
{
return false;
}
}


Login Process

Now we are ready for the actual login process. The first step is when the User has entered the Username and Password fields and has clicked on btnSubmitLogin. The following will occur:

- Credentials are checked (user is not logged in).
- A random code (Token) is generated and sent.
- UserName and Token are saved to Session variables.
- plcLoginArea.visible is set to false, plcTokenEntry.visible is set to true.

btnSubmitLogin_Click (VB)
Protected Sub btnSubmitLogin_Click(ByVal sender As Object, ByVal e As System.EventArgs)
' Validate the User (this does not log the user in)
If Membership.ValidateUser(txtUserName.Text, txtPassword.Text) Then
' Generate 4 character Token
Dim token As String = GenerateToken(4)
' Get the Mobile phone numer for this user (in this example hard coded)
Dim number As String = "0123456789"

' Try sending the SMS
If SendSMS(token, number) Then
' Hide and Show the PlaceHolders
plcTokenEntry.Visible = True
plcLoginArea.Visible = False
' Save values to Session Variables
HttpContext.Current.Session.Add("Username", txtUserName.Text)
HttpContext.Current.Session.Add("Token", token)
Else
' TODO Error sending SMS, give feedback, Log or whatever (or do this in the SendSMS method)
End If
Else
' TODO Verification failed, let the User know
End If
End Sub


btnSubmitLogin_Click (C#)
protected void btnSubmitLogin_Click(object sender, EventArgs e)
{
// Validate the User (this does not log the user in)
if (System.Web.Security.Membership.ValidateUser(txtUserName.Text, txtPassword.Text))
{
// Generate 4 character Token
string token = GenerateToken(4);
// Get the Mobile phone numer for this user (in this example hard coded)
string number = "0123456789";

// Try sending the SMS
if (SendSMS(token, number))
{
// Hide and Show the PlaceHolders
plcTokenEntry.Visible = true;
plcLoginArea.Visible = false;
// Save values to Session Variables
HttpContext.Current.Session.Add("Username", txtUserName.Text);
HttpContext.Current.Session.Add("Token", token);
}
else
{
// TODO Error sending SMS, give feedback, Log or whatever (or do this in the SendSMS method)
}
}
else
{
// TODO Verification failed, let the User know
}
}


The user should have received and SMS with the Token, and they can enter it into the text box, when they click on btnSubmitToken the following happens:

- The entered Token is verified with the token saved in the Session Variable.
- If the Tokens match the user is logged in by setting a Auth Cookie (using the UserName stored in the session).

btnTokenEntry_Click (VB)
Protected Sub btnTokenEntry_Click(ByVal sender As Object, ByVal e As System.EventArgs)
Dim token As String = HttpContext.Current.Session("Token").ToString()
If Not txtToken.Text = token Then
' TODO Token does not match, inform the User
Else
Dim userName As String = HttpContext.Current.Session("Username").ToString()
' Log the user in by setting the cookie
FormsAuthentication.SetAuthCookie(userName, False)
End If
End Sub


btnTokenEntry_Click (C#)
protected void btnTokenEntry_Click(object sender, EventArgs e)
{
string token = HttpContext.Current.Session["Token"].ToString();
if (!(txtToken.Text == token))
{
// TODO Token does not match, inform the User
}
else
{
string userName = HttpContext.Current.Session["Username"].ToString();
// Log the user in by setting the cookie
System.Web.Security.FormsAuthentication.SetAuthCookie(userName, false);
}
}



This is all very simple and needs a bit of work to make it user friendly, but the basics are there for a working two-factor authentication process which is very simple to implement in any website.

Shortcomings:
  • If the user closes their browser during the login process, the session has been lost and the whole process will need to be repeated as the sent code is no longer valid. (One option is to save the details to a database instead of to the session.)


Comments from Ocean10000
  • The seeding mechanism for generating the token can produce duplicate codes.*
  • In server farms you will need a server farm friendly form of Session-State [msdn.microsoft.com].
  • You can also choose to send the token to an email address instead of a Text Message (SMS).
* to force a new seed every time I have set the system to wait 20 ticks as the New Random() uses the current time as the seed. (In a loop with shorter times I was getting dupes).

Possible improvements:
  • Use Validators to check input (you should do this)
  • Use Ajax to show progress spinner while sending an SMS (this can sometimes take a few seconds)


Feedback please
Any comments, criticism, possible improvements or additions to this code would be greatly appreciated.

 

explorador




msg:4198514
 2:33 pm on Sep 8, 2010 (gmt 0)

Any comments, criticism, possible improvements or additions to this code would be greatly appreciated.


I like the idea, very practical but I see it depends on two systems (web and sms). In case of a failure on the web is logical nothing is accesible... but in case the sms system fails, stolen phone, damaged or just no signal or battery, the web is still working but I will be unable to login. Where I live there have been times where the sms system fails or delays the messages for about one or two hours, nothing is perfect.

Anyway I really like the idea.

Manish Singh




msg:4198727
 7:01 pm on Sep 8, 2010 (gmt 0)

Very similar system is used by my local bank. The only difference is

- they send access code to both Email and SMS.
- the access code is valid for a certain period of time (meaning, if I log out and log in again, I can use the old access code for next few hours).

At times the SMS gets delayed or my phone is out of signal range. In those times email comes in handy.

A problem that I have faced with this solution is that sometimes both SMS and Email get delayed (even a 1 minute delay in annoying when you simply want to login), so I end up generating multiple access codes. All of those different access codes arrive at the same time and then you are confused as to which one is the latest one.

I do not know the exact costs but you may also want to check the verisign identity protection system.

marcel




msg:4199136
 7:07 am on Sep 9, 2010 (gmt 0)

but in case the sms system fails, stolen phone, damaged or just no signal or battery, the web is still working but I will be unable to login

Yes, unfortunately this is one major drawback, but not only this technique, a SecurId token for example can also be lost or damaged (although it isn't susceptible to mobile network outages). We're looking into some kind of backdoor which can be manually opened when necessary.

The mobile phone number can be changed for the user by the Administrator though (in case of phone loss). Meaning the admin can login, change the users mobile phone number and the User can try again.

they send access code to both Email and SMS

We looked into that as well, but unfortunately most of the users we are catering to use the same password for their mail as for this application. (no matter how often we tell them that they shouldn't...)

the access code is valid for a certain period of time

This is also an option the customer has mentioned, more due to cost than any other reason.

The problem I have with this method is that it totally removes the security they first asked for, which is a OTP (one time password) system. But in many case this would be a very acceptable solution.

I do not know the exact costs but you may also want to check the verisign identity protection system.

Thanks, I hadn't yet heard of that system, we'll look into it.

Global Options:
 top home search open messages active posts  
 

Home / Forums Index / Microsoft / Microsoft IIS Web Server and ASP.NET
rss feed

All trademarks and copyrights held by respective owners. Member comments are owned by the poster.
Home ¦ Free Tools ¦ Terms of Service ¦ Privacy Policy ¦ Report Problem ¦ About ¦ Library ¦ Newsletter
WebmasterWorld is a Developer Shed Community owned by Jim Boykin.
© Webmaster World 1996-2014 all rights reserved