简体   繁体   中英

Form Authentication in ASP.net

Greetings of the day! I'm using Form Authentication, in registration it is OK, but while login, it's doing wrong, it is just doing opposite. In Registration, suppose I entered password 123, now it will convert that password using form-authentication, and save in DB, now while login, if user enter 123 then it will be changed, and try to match with the stored one in DB. In my case, it is doing opposite, if both password match, it shows, custom error message..and if not then increase counter variable for locking account

Please go over my code, and help me out....

Database:-

CREATE TABLE [dbo].[tblUsers](
    [ID] [int] IDENTITY(1,1) NOT NULL,
    [UserName] [nvarchar](15) NULL,
    [Password] [nvarchar](15) NULL,
    [Email] [nvarchar](200) NULL,
    [RetryAttempts] [int] NULL,
    [IsLocked] [bit] NULL,
    [LockedDateTime] [datetime] NULL,
PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]



CREATE proc [dbo].[sp_RegisterUser]
@UserName nvarchar(15),
@Password nvarchar(15),
@Email nvarchar(200)
As
Begin
Declare @Count int
Declare @ReturnCode int

Select @Count= COUNT(UserName) from tblUsers where UserName=@UserName
if(@Count=1)

Begin
Set @ReturnCode=-1
End

else
Begin
Set @ReturnCode=1
insert into tblUsers(UserName,Password,Email) values(@UserName,@Password,@Email)
End
Select @ReturnCode as ReturnValue
End


CREATE proc [dbo].[SP_AuthenticateUser]
@UserName nvarchar(15),
@Password nvarchar(15)
As
Begin
    Declare @Count int
    Declare @RetryCount int
    Declare @AccountLocked bit

    Select @AccountLocked= ISNULL(IsLocked,0)  from tblUsers where UserName=@UserName

    If(@AccountLocked=1)
    Begin
        Select 1 as AccountLocked,0 as Authenticate,0 as RetryAttempts
    End

    Else
    Begin
    Select @Count= COUNT(UserName) from tblUsers where UserName=@UserName and Password=@Password
    If(@Count=1)
    Begin
        Select 0 as AccountLocked,1 as Authenticate,0 as RetryAttempts
    End

    Else
    Begin
        Select @RetryCount=ISNULL(RetryAttempts,0) from tblUsers where UserName=@UserName
        Set @RetryCount=@RetryCount+1
    If(@RetryCount<=3)
    Begin
        Update tblUsers set RetryAttempts=@RetryCount where UserName=@UserName
        Select 0 as AccountLocked,0 as Authenticate,@RetryCount as RetryAttempts
    End
    Else
    Begin
        Update tblUsers set IsLocked=1,LockedDateTime=GETDATE() where UserName=@UserName
        Select 1 as AccountLocked,0 as Authenticate,0 as RetryAttempts
    End
    End
    End
End

Design:-

Registration_Page-

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Registration.aspx.cs" Inherits="Registration" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family:Arial">
<table style="border: 1px solid black">
    <tr>
        <td colspan="2">
            <b>User Registration</b>
        </td>
    </tr>
    <tr>
        <td>
            User Name
        </td>    
        <td>
            :<asp:TextBox ID="txtUserName" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorusername" 
            runat="server" ErrorMessage="User Name required" Text="*"
            ControlToValidate="txtUserName" ForeColor="Red">
            </asp:RequiredFieldValidator>
        </td>    
    </tr>
    <tr>
        <td>
            Password
        </td>    
        <td>
            :<asp:TextBox ID="txtPassword" TextMode="Password" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorPassword" 
            runat="server" ErrorMessage="Password required" Text="*"
            ControlToValidate="txtPassword" ForeColor="Red">
            </asp:RequiredFieldValidator>
        </td>    
    </tr>
    <tr>
        <td>
            Confirm Password
        </td>    
        <td>
            :<asp:TextBox ID="txtConfirmPassword" TextMode="Password" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorConfirmPassword" 
            runat="server" ErrorMessage="Confirm Password required" Text="*"
            ControlToValidate="txtConfirmPassword" ForeColor="Red" 
            Display="Dynamic"></asp:RequiredFieldValidator>
            <asp:CompareValidator ID="CompareValidatorPassword" runat="server" 
            ErrorMessage="Password and Confirm Password must match"
            ControlToValidate="txtConfirmPassword" ForeColor="Red" 
            ControlToCompare="txtPassword" Display="Dynamic"
            Type="String" Operator="Equal" Text="*">
            </asp:CompareValidator>
        </td>    
    </tr>
    <tr>
        <td>
            Email
        </td>    
        <td>
            :<asp:TextBox ID="txtEmail" runat="server">
            </asp:TextBox>
            <asp:RequiredFieldValidator ID="RequiredFieldValidatorEmail" 
            runat="server" ErrorMessage="Email required" Text="*"
            ControlToValidate="txtEmail" ForeColor="Red"
            Display="Dynamic"></asp:RequiredFieldValidator>
            <asp:RegularExpressionValidator ID="RegularExpressionValidatorEmail" 
            runat="server" ErrorMessage="Invalid Email" ControlToValidate="txtEmail"
            ForeColor="Red" Display="Dynamic" Text="*"
            ValidationExpression="\w+([-+.']\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*">
            </asp:RegularExpressionValidator>
        </td>    
    </tr>
    <tr>
        <td>

        </td>    
        <td>
            <asp:Button ID="btnRegister" runat="server" Text="Register" 
            onclick="btnRegister_Click"/>
        </td>    
    </tr>
    <tr>
        <td colspan="2">
            <asp:Label ID="lblMessage" runat="server" ForeColor="Red">
            </asp:Label>
        </td>    
    </tr>
    <tr>
        <td colspan="2">
            <asp:ValidationSummary ID="ValidationSummary1" ForeColor="Red" runat="server" />
        </td>    
    </tr>
</table>
</div>
    </form>
</body>
</html>

Login Page:_

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="_Default" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div style="font-family:Arial">
<table style="border: 1px solid black">
    <tr>
        <td colspan="2">
            <b>Login</b>
        </td>
    </tr>
    <tr>
        <td>
            User Name
        </td>    
        <td>
            :<asp:TextBox ID="txtUserName" runat="server">
            </asp:TextBox>
        </td>    
    </tr>
    <tr>
        <td>
            Password
        </td>    
        <td>
            :<asp:TextBox ID="txtPassword" TextMode="Password" runat="server">
            </asp:TextBox>
        </td>    
    </tr>
    <tr>
        <td>
           <asp:CheckBox ID="chk_boxRememberMe" runat="server" Text="Remember Me" />         
        </td>    
        <td>
            <asp:Button ID="btnLogin" runat="server" Text="Login" OnClick="btnLogin_Click" />
        </td>    
    </tr>
    <tr>
        <td>
            <asp:Label ID="lblMessage" runat="server" ForeColor="Red"></asp:Label>
        </td>
    </tr>
</table>
<br />
<a href="Registration/Registration.aspx">Click here to register</a> 
if you do not have a user name and password.
</div>

    </form>
</body>
</html>

Code Behind:-

Registration Page:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Security;
public partial class Registration : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void btnRegister_Click(object sender, EventArgs e)

    {
        if (Page.IsValid)
        {
            string CS = ConfigurationManager.ConnectionStrings["con"].ConnectionString;

            using (SqlConnection Conn=new SqlConnection(CS))
            {
                SqlCommand cmd = new SqlCommand("sp_RegisterUser",Conn);
                cmd.CommandType = CommandType.StoredProcedure;

                SqlParameter UserName = new SqlParameter("@UserName",txtUserName.Text);
                string EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "SHA1");
                SqlParameter Password = new SqlParameter("@Password", EncryptPassword);

                SqlParameter Email = new SqlParameter("@Email", txtEmail.Text);

                cmd.Parameters.Add(UserName);
                cmd.Parameters.Add(Password);
                cmd.Parameters.Add(Email);

                Conn.Open();

                int ReturnCode=(int) cmd.ExecuteScalar();

                if (ReturnCode==-1)
                {
                    lblMessage.Text = "User Name alredy exists";
                }
                else
                {
                    Response.Redirect("~/Login.aspx");
                }
            }
        }
    }
}

Login Page:-

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Web.Security;


public partial class _Default : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {

    }

    protected void btnLogin_Click(object sender, EventArgs e)
    {
        //Login_WebConfig();
        // Login_DataBase();

        if (AuthenticateUser(txtUserName.Text, txtPassword.Text))
        {
            FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, chk_boxRememberMe.Checked);
        }
        else
        {
            lblMessage.Text = "Invalid username/password";
        }
    }

    //protected void Login_WebConfig()
    //{
    //    if (FormsAuthentication.Authenticate(txtUserName.Text, txtPassword.Text))
    //    {
    //        FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, chk_boxRememberMe.Checked);
    //    }
    //    else
    //    {
    //        lblMessage.Text = "Invalid user name/password";
    //    }
    //}

    protected void Login_DataBase()
    {

    }

    private bool AuthenticateUser(string username, string password)
    {
        string CS = ConfigurationManager.ConnectionStrings["con"].ConnectionString;
        using (SqlConnection Conn = new SqlConnection(CS))
        {
            SqlCommand cmd = new SqlCommand("SP_AuthenticateUser", Conn);
            cmd.CommandType = CommandType.StoredProcedure;

            string EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(password, "SHA1");

            SqlParameter paramUserName = new SqlParameter("@UserName", username);
            SqlParameter paramPassword = new SqlParameter("@Password", EncryptPassword);

            cmd.Parameters.Add(paramUserName);
            cmd.Parameters.Add(paramPassword);
            Conn.Open();

            int ReturnCode = (int)cmd.ExecuteScalar();
            return ReturnCode == 1;


        }
    }
}

Web Config:-

<?xml version="1.0"?>

<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->

<configuration>

    <system.web>
      <compilation debug="true" targetFramework="4.5" />
      <httpRuntime targetFramework="4.5" />
      <authentication mode="Forms">
        <forms loginUrl="Login.aspx" defaultUrl="Welcome.aspx" timeout="2" protection="All">
          <!--<credentials passwordFormat="Clear">
            <user name="rkbisht" password="1234"/>
          </credentials>-->
        </forms>
      </authentication>
      <authorization>
        <deny users="?"/>
      </authorization>
  </system.web>

    <appSettings>
      <add key="ValidationSettings:UnobtrusiveValidationMode" value="None" />
    </appSettings>

  <connectionStrings>
    <add name="con" connectionString="Data Source=.;Initial Catalog=Security_Learning;Integrated Security=true"/>
  </connectionStrings>

</configuration>

Basically, you are not using the right tool for the job.

You are not acutually using FormsAuthentication and you are also not saving the hash in a config file so FormsAuthentication.HashPasswordForStoringInConfig is not what you should be using.

It is worth taking the time to get this right. Here is a starting point:

https://docs.microsoft.com/en-us/aspnet/core/security/data-protection/consumer-apis/password-hashing

Note, you should be using a hash and a salt, the salt should be unique to each user.

Personally I use the Microsoft.AspNetCore.Cryptography.KeyDerivation nuget package with an implementation similar to the following:

Helper Class

using Microsoft.AspNetCore.Cryptography.KeyDerivation;
using Resources;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Web;

public static class SecurityHelper
{
     public static int DefaultIterations = 10000

     //KeyDerivation.Pbkdf2
     /// <summary>
     /// Generates a random salt
     /// </summary>
     /// <returns>A byte array containing a random salt</returns>
     public static byte[] GetRandomSalt()
     {
         byte[] saltBytes = new byte[32];
         RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
         rng.GetNonZeroBytes(saltBytes);            
         return saltBytes;
     }

     public static string GeneratePasswordHash(string plainPassword, int iterations, out string generatedRandomSalt)
     {
         generatedRandomSalt = Convert.ToBase64String(GetRandomSalt());
         return Convert.ToBase64String(ComputeHash(plainPassword, generatedRandomSalt, iterations));
     }

     public static string GetPasswordHash(string plainPassword, string existingSalt, int iterations)
     {
         return Convert.ToBase64String(ComputeHash(plainPassword, existingSalt, iterations));
     }



    private static byte[] ComputeHash(string plainText, string salt, int iterations)
    {
         return KeyDerivation.Pbkdf2(
         password: plainText,
            salt: Convert.FromBase64String(salt),
            prf: KeyDerivationPrf.HMACSHA256,
            iterationCount: iterations,
            numBytesRequested: 32);
    }       

 }

Your workflow now changes:

  • When you save the password, you need to save the salt as well
  • When authenticating the user, get the user by UserName from the database, calculate the hash for the entered password, using the saved salt, compareing the result with the saved hash.

Roughly

//Registration
using (SqlConnection Conn=new SqlConnection(CS))
{
    SqlCommand cmd = new SqlCommand("sp_RegisterUser",Conn);
    cmd.CommandType = CommandType.StoredProcedure;

    SqlParameter UserName = new SqlParameter("@UserName",txtUserName.Text);
    string Salt = string.Empty;
    string Password = SecurityHelper.GeneratePasswordHash(txtPassword.Text, SecurityHelper.DefaultIterations, out salt);
    ;
    SqlParameter Password = new SqlParameter("@Password", Password);
    SqlParameter Email = new SqlParameter("@Email", txtEmail.Text);
    SqlParameter Salt = new SqlParameter("@Salt", Salt);
    cmd.Parameters.Add(UserName);
    cmd.Parameters.Add(Password);
    cmd.Parameters.Add(Email);

    Conn.Open();

    int ReturnCode=(int) cmd.ExecuteScalar();

    if (ReturnCode==-1)
    {
        lblMessage.Text = "User Name alredy exists";
    }
    else
    {
         Response.Redirect("~/Login.aspx");
    }
}

//Log In
private bool AuthenticateUser(string username, string password)
{
    //Get the following from your database based on username
    string savedHash = //fromDB;
    string savedSalt = //fromDb;

    return (SecurityHelper.GetPasswordHash(password, savedSalt, SecurityHelper.DefaultIterations) == tempUser.Password)                
}

I've simplified this somewhat. I also save the iteration against the user in the database, in case we need to increase the default iterations.

My last bit of adivce, so some reading on why "salting" is a good thing.

Your authentication stored procedure ( SP_AuthenticateUser ) returns 3 columns but you are calling it using ExecuteScalar . You need to get the dataset and check the second column.

private bool AuthenticateUser(string username, string password)
{
    string CS = ConfigurationManager.ConnectionStrings["con"].ConnectionString;
    using (SqlConnection Conn = new SqlConnection(CS))
    {
        SqlCommand cmd = new SqlCommand("SP_AuthenticateUser", Conn);
        cmd.CommandType = CommandType.StoredProcedure;

        string EncryptPassword = FormsAuthentication.HashPasswordForStoringInConfigFile(password, "SHA1");

        SqlParameter paramUserName = new SqlParameter("@UserName", username);
        SqlParameter paramPassword = new SqlParameter("@Password", EncryptPassword);

        cmd.Parameters.Add(paramUserName);
        cmd.Parameters.Add(paramPassword);
        Conn.Open();

        var reader = cmd.ExecuteReader();
        reader.Read();
        return reader["Authenticate"] as bool;
    }
}

Also, make sure you reset the retry counter after successful authentication.

Select @Count= COUNT(UserName) from tblUsers where UserName=@UserName and Password=@Password
If(@Count=1)
Begin
    Update tblUsers set RetryAttempts = 0 where UserName = @UserName
    Select 0 as AccountLocked,1 as Authenticate,0 as RetryAttempts
End

Your code has a few other problems too, but those two are likely to be causing the behavior noted in your question.

Here is a better way to hash your password .

The user will have to keep signing on unless you set a forms auth cookie. Click here for ideas on how .

You probably don't need credentials in your web.config. You only need to put them there if you intend to use FormsAuthentication.Authenticate . You seem to be using a database instead.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM