Login using Entity with password encrypted as varbinary

Asked

Viewed 1,819 times

4

I did the user password encryption and in the Password field of my table I went from Varchar to Varbinary, in the web part of my system it is all right only that in the windows part I am using the Entity and do not know how to validate the user password, do not know if the EF has something of the type or I have to encrypt the password entered and pass by parameter to perform this query...

    public static User ValidateUser(string UserName, string Password, ObjEntities cx)
    {
        var o = from c in cx.User
                where c.UserName == UserName && c.Password == Password
                select c;

        if (o.ToList().Count > 0)
            return o.Single();
        else
            return null;
    }
  • How did you do this "user password encryption"? Would it have been a hash? (type HashBytes) If that’s the case, you have to re-hash the password and compare it to the saved value (I don’t know how to do this via LINQ, but via SQL it should surely be easy - see linked response in Soen).

  • I did it right in my Store Procedure - PWDENCRYPT(@Password)

  • That one Password passed as parameter is a hash or is the password itself typed on screen?

  • If you used PWDENCRYPT(senha) to hash, you must use PWDCOMPARE(senha, hash) to compare. In SQL it would be something like where UserName = c.UserName and PWDCOMPARE(Password, c.Password). I do not know how to do via LINQ (I have no practical experience with C#). P.S. That question on the Soen explains how to do.

  • PWDENCRYPT is a Storedprocedure? and the username is unique?

  • 1

    This way of doing is somewhat out of the standard of ASP.NET MVC applications. Have you ever thought about using ASP.NET Membership or ASP.NET Identity?

  • @Ciganomorrisonmendez Although I agree 100% with your suggestion (authentication and authorization are complex issues, let alone solutions ad-hoc better), it is the exception that - by the description of OP ("in the web part of my system is all right only that in the windows part...") - it seems to me that it is about interoperability between two different systems (not necessarily on the same platform, but I may be mistaken). Adopting the standards of one would imply needing to adapt the other (which from a security point of view is not a bad idea, but anyway is not something that would come without costs).

  • The user registration is performed by the web, in the windows part the user only accesses the system, at first we would develop the system in MVC using EF also but for the need of a rapid development we decided to develop in ASP.NET using ADO and in the windows WPF part using EF, thanks for the attention...

  • 1

    @mgibsonbr That’s right... but it’s the validation of an autonomous system. That’s why I thought about Membership or Identity. Anyway, the suggestion is.

  • 1

    @Jhonas Regardless of these choices, Membership and Identity still serve the purpose well. I think it’s worth some research.

  • I will search yes, vlw by tip!

Show 6 more comments

2 answers

4


Simple example:

I would create 2 Stored Procedure for the Table User, and would carry within the Entity Framework.

Create Table User

CREATE TABLE [dbo].[User](
    [UserId] [int] IDENTITY(1,1) NOT NULL,
    [UserName] [varchar](50) NULL,
    [Password] [varbinary](128) NULL,
 CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]

Stored Procedure

CREATE PROCEDURE SP_Insert_User 
(
    @UserName varchar(50),
    @Password varchar(30)
)
AS
BEGIN
    INSERT INTO [User]([UserName], [Password]) VALUES(@UserName, PWDENCRYPT(@Password));
    SELECT [UserId], [UserName], [Password] FROM [User] WHERE [UserId] = @@IDENTITY;
END

CREATE PROCEDURE SP_User_Verify
(   
    @UserName varchar(50),
    @Password varchar(30)
)
AS
BEGIN
    SELECT * FROM [User] WHERE [User].[UserName] = @UserName AND 
                               PWDCOMPARE(@Password,[User].[Password]) = 1
END

To SP_Insert_User to insert new users and the SP_User_Verify to verify the existence of the user with the return of his data.

In your Model I import both Stored Procedure in that way

inserir a descrição da imagem aqui

Choose the two with the respective names that within the Context will be created two functions back that way:

public partial class ObjEntities : DbContext
{
    public ObjEntities()
        : base("name=ObjEntities") { }

    public DbSet<User> User { get; set; }   
    public virtual ObjectResult<SP_Insert_User_Result> SP_Insert_User(string userName, string password)
    {
        var userNameParameter = userName != null ?
            new ObjectParameter("UserName", userName) :
            new ObjectParameter("UserName", typeof(string));

        var passwordParameter = password != null ?
            new ObjectParameter("Password", password) :
            new ObjectParameter("Password", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<SP_Insert_User_Result>("SP_Insert_User", userNameParameter, passwordParameter);
    }
    public virtual ObjectResult<SP_User_Verify_Result> SP_User_Verify(string userName, string password)
    {
        var userNameParameter = userName != null ?
            new ObjectParameter("UserName", userName) :
            new ObjectParameter("UserName", typeof(string));

        var passwordParameter = password != null ?
            new ObjectParameter("Password", password) :
            new ObjectParameter("Password", typeof(string));

        return ((IObjectContextAdapter)this).ObjectContext.ExecuteFunction<SP_User_Verify_Result>("SP_User_Verify", userNameParameter, passwordParameter);
    }
}

How to use:

class Program
{
    static void Main(string[] args)
    {
        using (ObjEntities cx = new ObjEntities())
        {
            SP_User_Verify_Result user = ValidateUser("USUARIO2", "SENHA2", cx);
        }
    }
    public static SP_User_Verify_Result ValidateUser(string UserName, string Password, ObjEntities cx)
    {
        return cx.SP_User_Verify(UserName, Password).FirstOrDefault<SP_User_Verify_Result>();
    }
}

Debug:

inserir a descrição da imagem aqui

If on that line SP_User_Verify_Result user = ValidateUser("USUARIO2", "SENHA2", cx); the variable user for null, so, no user was found, consequently, unauthorized user.

  • 1

    Vlw Harry, it worked!

1

Translated/adapted response of this reply in Soen

What you need to do is encapsulate PWDCOMPARE in an UDF (user-defined function) in your database, and then call this UDF through your DataContext.

http://msdn.microsoft.com/pt-br/library/bb399416.aspx

That is, run this in your database:

CREATE FUNCTION fn_PWDCOMPARE (@pwd NVARCHAR(MAX),@pwdhash NVARCHAR(MAX))
RETURNS BIT
BEGIN
  RETURN PWDCOMPARE(@pwd, @pwdhash)
END

So add that to your DataContext in Visual Studio and call it that way:

var o = from c in cx.User
            where c.UserName == UserName && 
                  db.fn_PWDCOMPARE(Password, c.Password)
            select c;

P.S. The function PWDENCRYPT is an old function, and its use is discouraged in new projects. If it is possible to change, consider using HASHBYTES instead of her (although reading the documentation, it seems to me that the hash functions supported are not safe to protect passwords... Unfortunately, I have no alternative to suggest).

  • Cool! I add the function in my Context using the Update Model from Database but I am not able to view it, appears all the tables and the Store Procedure but this no, I have to do something different to use this function?

  • @Jhonas Unfortunately, I can’t help you with that. The little C# I know is on Mono, I never used the Microsoft environment... I’m afraid you’ll have to wait for someone else to answer.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.