This article is an extension to my previous articles:

  1. Generation of CAPTCHA Image Using Generic Handler for Login Page
  2. Stored Procedure For Login Page and Custom Error Handling

1. Filtering from login page

If the filtering of the XSS attack occurred from the login page then:

Input controls should have a filtertextbox extender as below.

  1. <asp:TextBox ID="txtLogin" runat="server" Width="175px" MaxLength="20" AutoCompleteType="Disabled"></asp:TextBox>
  2. <asp:FilteredTextBoxExtender ID="fltr_txtLogin" runat="server" FilterType="UppercaseLetters,LowercaseLetters,Numbers" TargetControlID="txtLogin">
  3. </asp:FilteredTextBoxExtender>
  4. <asp:TextBox ID="txtPassword" runat="server" Width="175px" MaxLength="20" AutoCompleteType="Disabled" TextMode="Password"></asp:TextBox>
  5. <asp:FilteredTextBoxExtender ID="fltr_txtPassword" runat="server" FilterType="Numbers, LowercaseLetters, UppercaseLetters, Custom" TargetControlID="txtPassword" ValidChars="@!_%$#"></asp:FilteredTextBoxExtender>
  6. <asp:TextBox ID="txtCode" runat="server" Width="175px" MaxLength="5" AutoCompleteType="Disabled"></asp:TextBox>
  7. <asp:FilteredTextBoxExtender ID="fltr_txtCode" runat="server" FilterType="Numbers"
  8. TargetControlID="txtCode"></asp:FilteredTextBoxExtender>

2. Alert user if other session is active on another machine for requested credentials.

And

3. Having single session per user means at a time only one session will be active for each user.

If someone has logged in from another machine using the same credentials then the user must be alerted about another active session on the credentials provided.



To maintain a single session, we have already added two columns in UserMaster. The IsLogin flag maintains a user's Login Status and UserSession column maintains a unique session id. If the user selects the OK button then a new session id is updated against the user details in UserMaster else returned to the login page.

Modified Login page aspx script

  1. <%@ Page Title="LOGIN" Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs"
  2. Inherits="Login" %>
  3. <%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" TagPrefix="asp" %>
  4. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  5. <head id="Head1" runat="server">
  6. <title>LOGIN</title>
  7. </head>
  8. <body>
  9. <form id="form1" runat="server">
  10. <h1>
  11. Login</h1>
  12. <table id="tblLogin" runat="server" width="40%" border="0" cellpadding="0" cellspacing="4"
  13. style="background-color: #cecece;" align="center">
  14. <tbody>
  15. <tr>
  16. <td align="center" colspan="2">
  17. <asp:Label ID="lblError" runat="server" ForeColor="Red"></asp:Label>
  18. <asp:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">
  19. </asp:ToolkitScriptManager>
  20. </td>
  21. </tr>
  22. <tr>
  23. <td width="30%" align="right">
  24. User ID :
  25. </td>
  26. <td width="70%">
  27. <asp:TextBox ID="txtLogin" runat="server" Width="175px" MaxLength="20" AutoCompleteType="Disabled"></asp:TextBox>
  28. <asp:RequiredFieldValidator ID="req_txtLogin" runat="server" ErrorMessage="Enter Login ID."
  29. ToolTip="Enter Login ID." ControlToValidate="txtLogin" Text="*" Display="Static"
  30. ForeColor="Red" ValidationGroup="vldLogin"></asp:RequiredFieldValidator>
  31. <asp:FilteredTextBoxExtender ID="fltr_txtLogin" runat="server" FilterType="UppercaseLetters,LowercaseLetters,Numbers"
  32. TargetControlID="txtLogin">
  33. </asp:FilteredTextBoxExtender>
  34. </td>
  35. </tr>
  36. <tr>
  37. <td align="right">
  38. Password :
  39. </td>
  40. <td>
  41. <asp:TextBox ID="txtPassword" runat="server" Width="175px" MaxLength="20" AutoCompleteType="Disabled"
  42. TextMode="Password"></asp:TextBox>
  43. <asp:RequiredFieldValidator ID="req_txtPassword" runat="server" ErrorMessage="Enter Password."
  44. ToolTip="Enter Password." ControlToValidate="txtPassword" Text="*" Display="Static"
  45. ForeColor="Red" ValidationGroup="vldLogin"></asp:RequiredFieldValidator>
  46. <asp:FilteredTextBoxExtender ID="fltr_txtPassword" runat="server" FilterType="Numbers, LowercaseLetters, UppercaseLetters, Custom"
  47. TargetControlID="txtPassword" ValidChars="@!_%$#">
  48. </asp:FilteredTextBoxExtender>
  49. </td>
  50. </tr>
  51. <tr>
  52. <td>
  53. </td>
  54. <td colspan="2" align="left">
  55. <div>
  56. <asp:UpdatePanel ID="UpdatePanel1" runat="server">
  57. <ContentTemplate>
  58. <asp:Image ImageUrl="ghCaptcha.ashx" runat="server" ID="imgCaptcha" />
  59. <asp:ImageButton ID="btnRefresh" runat="server" Width="10px" Height="10px" ImageUrl="~/refresh.jpg"
  60. OnClick="btnRefresh_Click" />
  61. </ContentTemplate>
  62. </asp:UpdatePanel>
  63. </div>
  64. </td>
  65. </tr>
  66. <tr>
  67. <td align="right">
  68. Enter Code :
  69. </td>
  70. <td>
  71. <asp:TextBox ID="txtCode" runat="server" Width="175px" MaxLength="5" AutoCompleteType="Disabled"></asp:TextBox>
  72. <asp:RequiredFieldValidator ID="req_txtCode" runat="server" ErrorMessage="Enter captcha code."
  73. ToolTip="Enter captcha code." ControlToValidate="txtCode" Text="*" Display="Static"
  74. ForeColor="Red" ValidationGroup="vldLogin"></asp:RequiredFieldValidator>
  75. <asp:FilteredTextBoxExtender ID="fltr_txtCode" runat="server" FilterType="Numbers"
  76. TargetControlID="txtCode">
  77. </asp:FilteredTextBoxExtender>
  78. </td>
  79. </tr>
  80. <tr>
  81. <td colspan="2" align="center">
  82. <asp:Button ID="btnLogin" runat="server" Text="Login" ValidationGroup="vldLogin"
  83. OnClick="btnLogin_Click" />
  84. <asp:ValidationSummary ID="ValidationSummary1" runat="server" ValidationGroup="vldLogin"
  85. ShowSummary="false" ShowMessageBox="true" HeaderText="You have received following errors." />
  86. </td>
  87. </tr>
  88. </tbody>
  89. </table>
  90. <table id="tblAlert" runat="server" width="40%" border="0" cellpadding="0" cellspacing="4"
  91. style="background-color: #cecece;" align="center" visible="false">
  92. <tr>
  93. <td align="center">
  94. This user is already logged in. Do you want to terminate other active session.
  95. </td>
  96. </tr>
  97. <tr>
  98. <td align="center">
  99. <asp:Button ID="btnOk" runat="server" Text="OK" OnClick="btnOk_Click" />
  100. <asp:Button ID="btnCancel" runat="server" Text="Cancel" OnClick="btnCancel_Click" />
  101. </td>
  102. </tr>
  103. </table>
  104. </form>
  105. </body>
  106. </html>

Login.aspx.cs Code behind

  1. #region " [ using ] "
  2. using System;
  3. using System.Configuration;
  4. using System.Data;
  5. using System.Data.SqlClient;
  6. using System.Web.UI;
  7. #endregion
  8. public partial class Login : System.Web.UI.Page
  9. {
  10. protected void Page_Load(object sender, EventArgs e)
  11. {
  12. if (!Page.IsPostBack)
  13. {
  14. UpdateCaptchaText();
  15. }
  16. }
  17. #region " [ Button Event ] "
  18. protected void btnRefresh_Click(object sender, ImageClickEventArgs e)
  19. {
  20. UpdateCaptchaText();
  21. }
  22. protected void btnLogin_Click(object sender, EventArgs e)
  23. {
  24. if (!string.Equals(txtCode.Text.Trim(), (string)Session["Captcha"]))
  25. {
  26. lblError.Text = "Enter correct code.";
  27. return;
  28. }
  29. lblError.Text = string.Empty;
  30. DataTable dtUser = new DataTable();
  31. string userSession = Guid.NewGuid().ToString();
  32. Session["UserSession"] = userSession;
  33. try
  34. {
  35. dtUser = checkUserLogin(userSession, "LOGIN");
  36. if (dtUser != null)
  37. {
  38. if (dtUser.Columns.Contains("RES"))
  39. {
  40. lblError.Text = dtUser.Rows[0][0].ToString();
  41. ClearPage();
  42. }
  43. else
  44. {
  45. Session["UserID"] = dtUser.Rows[0]["UserID"];
  46. Session["UserName"] = dtUser.Rows[0]["UserName"];
  47. Session["LastLogin"] = dtUser.Rows[0]["LastLogin"];
  48. if (string.Equals(dtUser.Rows[0]["IsLogin"].ToString(), "True"))
  49. {
  50. tblAlert.Visible = true;
  51. tblLogin.Visible = false;
  52. }
  53. else
  54. {
  55. Response.Redirect("~/Welcome.aspx");
  56. }
  57. }
  58. }
  59. else
  60. {
  61. ClearPage();
  62. lblError.Text = "Unexpected error.";
  63. }
  64. }
  65. catch
  66. {
  67. throw;
  68. }
  69. finally
  70. {
  71. dtUser.Dispose();
  72. }
  73. }
  74. protected void btnOk_Click(object sender, EventArgs e)
  75. {
  76. checkUserLogin(Session["UserSession"].ToString(), "CHANGELOGIN");
  77. Response.Redirect("~/Welcome.aspx");
  78. }
  79. protected void btnCancel_Click(object sender, EventArgs e)
  80. {
  81. Response.Redirect("~/login.aspx");
  82. }
  83. #endregion
  84. #region " [ Private Function ] "
  85. private DataTable checkUserLogin(string userSession, string mode)
  86. {
  87. DataSet dsData = new DataSet();
  88. SqlConnection sqlCon = null;
  89. SqlDataAdapter sqlCmd = null;
  90. try
  91. {
  92. using (sqlCon = new SqlConnection(ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString))
  93. {
  94. sqlCmd = new SqlDataAdapter("USP_UserLogin", sqlCon);
  95. sqlCmd.SelectCommand.CommandType = CommandType.StoredProcedure;
  96. sqlCmd.SelectCommand.Parameters.AddWithValue("@loginID", txtLogin.Text.Trim());
  97. sqlCmd.SelectCommand.Parameters.AddWithValue("@password", txtPassword.Text.Trim());
  98. sqlCmd.SelectCommand.Parameters.AddWithValue("@sessionID", userSession);
  99. sqlCmd.SelectCommand.Parameters.AddWithValue("@mode", mode);
  100. sqlCon.Open();
  101. sqlCmd.Fill(dsData);
  102. sqlCon.Close();
  103. }
  104. }
  105. catch
  106. {
  107. throw;
  108. }
  109. return dsData.Tables[0];
  110. }
  111. private void ClearPage()
  112. {
  113. txtCode.Text = string.Empty;
  114. txtPassword.Text = string.Empty;
  115. txtCode.Text = string.Empty;
  116. UpdateCaptchaText();
  117. }
  118. private void UpdateCaptchaText()
  119. {
  120. txtCode.Text = string.Empty;
  121. Random randNum = new Random();
  122. //Store the captcha text in session to validate
  123. Session["Captcha"] = randNum.Next(10000, 99999).ToString();
  124. imgCaptcha.ImageUrl = "~/ghCaptcha.ashx?" + Session["Captcha"];
  125. }
  126. #endregion
  127. }

Expected result

1. If the user has logged into the application and then if the same credentials are used for the login on another machine then the user will receive an alert that another session is active, do you want to terminate it.



2. If the OK button is clicked then a new session is updated in usermaster table otherwise the page is refreshed.

Also on the home.aspx page it is required to check that the sessionID stored in the database and the sessionID received from the current user are the same. This ensures that only one session is active per user.

The following is the Page Load event of the Home.aspx page.

  1. protected void Page_Load(object sender, EventArgs e)
  2. {
  3. if (!Page.IsPostBack)
  4. {
  5. BindUserData();
  6. if (!CheckUserSession())
  7. {
  8. Response.Redirect("~/login.aspx");
  9. }
  10. }
  11. }