简体   繁体   English

我的REST API密码/身份验证策略是否正确?

[英]Is my password/authentication strategy for a REST API good?

Okay, good is subjective, but it fits better in the title. 好的,好的是主观的,但标题更合适。 What I really want to know is if there are any glaring deficiencies in this password management/authentication strategy. 我真正想知道的是,此密码管理/身份验证策略中是否存在任何明显的缺陷。 I am also curious about performance. 我也对性能感到好奇。 I am trying to use PBKDF2 to authenticate but I am not really sure if that is a good idea for a RESTful web service. 我正在尝试使用PBKDF2进行身份验证,但我不确定这是否是RESTful Web服务的好主意。 I can find some parts of this question answered here and there but I have never found one comprehensive top to bottom answer. 我可以在这里到那里找到该问题的某些部分,但是我从未找到一个完整的自上而下的答案。 Here goes, this is my entire strategy. 这就是我的整个策略。

Background: 背景:

  • Service is an ASP.NET Web Api project in C# 服务是C#中的ASP.NET Web Api项目
  • Backend is MS SQL server 后端是MS SQL Server
  • Credentials are sent over HTTPS using Basic authentication, ie 'authorization: Basic username:password' 使用基本身份验证通过HTTPS发送凭据,即“授权:基本用户名:密码”
  • API will be consumed by Andriod app, ios app and web site API将由Andriod应用程序,iOS应用程序和网站使用

First, the backend: 首先,后端:

create table [dbo].[User] (
    [Name] varchar(50) collate SQL_Latin1_General_CP1_CI_AI not NULL primary key clustered,
    [Password] varchar(28) collate SQL_Latin1_General_CP1_CS_AS not NULL,
    [Salt] varchar(28) not NULL)

the stored procedure to create the user: 存储过程创建用户:

create procedure [dbo].[User_Create] 
    @Name varchar(50),
    @Salt varchar(50),
    @Password varchar(50)
as 
    insert into [dbo].[User]([Name], [Salt], [Password])
    values(@Name, @Salt, @Password)

and the stored procedure to get the user: 和存储过程来获取用户:

create procedure [dbo].[User_Get] 
    @Name varchar(50)
as 
    select *
    from [dbo].[User]
    where [Name] = @Name

With the backend I am curious if the data types selected for the name, password and salt are appropriate. 对于后端,我很好奇,为名称,密码和盐选择的数据类型是否合适。

This is the code that creates a new user and persists it to the backend. 这是创建新用户并将其持久化到后端的代码。 This is probably what I am most concerned about, both from a security stand point as well as performance. 从安全角度和性能上来说,这可能是我最担心的问题。

public void CreateUser(string username, string password)
{
    int hashLength = 20;
    int saltLength = 20;
    int hashIterations = 1000;
    using(SqlConnection connection = new SqlConnection(this._ConnectionString))
    using (SqlCommand command = new SqlCommand("[dbo].[User_Create]"))
    {
        Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, saltLength, hashIterations);
        string salt = Convert.ToBase64String(pbkdf2.Salt);
        string hashPassword = Convert.ToBase64String(pbkdf2.GetBytes(hashLength));

        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.AddWithValue("@Name", username);
        command.Parameters.AddWithValue("@Salt", salt);
        command.Parameters.AddWithValue("@Password", hashPassword);
        connection.Open();
        command.ExecuteNonQuery();
    }
}

And here is the code to authenticate the user. 这是验证用户身份的代码。 This essentially just executes the stored procedure, calls ValidatePassword to perform the PBKDF2 and returns a User object back to the Web API message handler which sets the Principal (roles have not been implemented yet): 这实际上只是执行存储过程,调用ValidatePassword执行PBKDF2,然后将User对象返回给Web API消息处理程序,该处理程序设置Principal(角色尚未实现):

public User AuthenticateUser(string username, string password)
{
    using (SqlConnection connection = new SqlConnection(this._ConnectionString))
    using (SqlCommand command = new SqlCommand("[dbo].[User_Get]"))
    {
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.AddWithValue("@Name", username);
        command.Connection = connection;

        connection.Open();
        using (SqlDataReader reader = command.ExecuteReader())
        {
            if (reader.Read() && this.ValidatePassword(password, reader["Salt"].ToString(), reader["Password"].ToString()))
            {
                return new User()
                    {
                        Name = username,
                        CustomerId = reader["CustomerId"].ToString()
                    };
            }
            else
            {
                return null;
            }
        }
    }
}

And here is ValidatePassword , which the above code relies on to validate the password (in case that wasn't obvious). 这是ValidatePassword ,上面的代码依靠它来验证密码(以防万一)。 I also want to make sure I got this one right. 我还想确保我正确了这一点。

private bool ValidatePassword(string password, string salt, string hashedPassword)
{
    int hashLength = 20;
    int hashIterations = 1000;
    byte[] saltBytes = Convert.FromBase64String(salt);
    Rfc2898DeriveBytes pbkdf2 = new Rfc2898DeriveBytes(password, saltBytes, hashIterations);

    byte[] hashBytes = pbkdf2.GetBytes(hashLength);
    string hash = Convert.ToBase64String(hashBytes);

    // Security Decisions For String Comparisons
    //
    // If you are making a security decision (such as whether to allow access to a system resource) based on the 
    // result of a string comparison or a case change, you should not use the invariant culture. Instead, you 
    // should perform a case-sensitive or case-insensitive ordinal comparison by calling a method that includes 
    // a StringComparison parameter and supplying either StringComparison.Ordinal or 
    // StringComparison.OrdinalIgnoreCase as an argument. Code that performs culture-sensitive string operations 
    // can cause security vulnerabilities if the current culture is changed or if the culture on the computer 
    // that is running the code differs from the culture that is used to test the code. In contrast, an ordinal 
    // comparison depends solely on the binary value of the compared characters.
    //
    // Source: http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.invariantculture.aspx

    return hash.Equals(hashedPassword, StringComparison.Ordinal);
}

highly opinionated answer. 高度自以为是的答案。

why do this yourself when there are many off the shelf (as in completely free and well tested) implementations? 当有很多现成的实现(如完全免费且经过良好测试的实现)时,为什么要自己做呢?

By all means do this as an exercise (it a fascinating area imo) but timing attacks are just the tip of the iceberg. 一定要把它当作练习(这是一个令人着迷的区域imo),但定时攻击只是冰山一角。

the first rule of security is if you are doing anything involving encryption yourself you are probably venturing into an area that you should ensure you do very little in. Eg use an owin or open auth provider or active directory. 安全性的第一条规则是,如果您自己进行涉及加密的任何事情,您可能正在冒险进入一个应该确保很少进行加密的区域。例如,使用owin或开放式身份验证提供程序或活动目录。 But step away from the weapon. 但是要远离武器。

dont become the next muppet to leak a ton of data by doing it yourself. 不要成为自己动手泄漏大量数据的下一个木偶。

have a look at federated security, saml tokens and sts providers 看一下联合安全,saml令牌和sts提供程序

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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