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

时间:2013-08-06 00:57:02

标签: c# sql security rest asp.net-web-api

好的,好的是主观的,但它在标题中更合适。我真正想知道的是这个密码管理/认证策略是否有任何明显的缺陷。我也对表现感到好奇。我正在尝试使用PBKDF2进行身份验证,但我不确定这对于RESTful Web服务是否是一个好主意。我可以在这里和那里找到这个问题的一些部分,但我从来没有找到一个全面的自上而下的答案。这就是我的整个策略。

背景:

  • Service是C#
  • 中的ASP.NET Web Api项目
  • 后端是MS SQL服务器
  • 凭据使用基本身份验证通过HTTPS发送,即“授权:基本用户名:密码”
  • API将由Andriod app,ios app和网站
  • 使用

首先,后端:

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)

创建用户的存储过程:

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)

以及获取用户的存储过程:

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

对于后端,我很好奇为名称,密码和盐选择的数据类型是否合适。

这是创建新用户并将其持久保存到后端的代码。从安全角度和表现来看,这可能是我最关心的。

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();
    }
}

以下是验证用户身份的代码。这基本上只是执行存储过程,调用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;
            }
        }
    }
}

这里是ValidatePassword,上面的代码依赖于验证密码(如果不明显的话)。我也想确保我把这个做对了。

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);
}

1 个答案:

答案 0 :(得分:0)

高度自以为是的答案。

为什么在有许多现成的(如完全免费且经过良好测试的)实施时自己这样做?

一定要把它作为一个练习(它是一个迷人的区域),但是时间攻击只是冰山一角。

第一个安全规则是,如果你自己正在做任何涉及加密的事情,你可能会冒险进入一个你应该确保你做得很少的领域。例如,使用一个owin或open auth提供者或活动目录。但是远离武器。

不要成为下一个通过自己动手泄漏大量数据的布偶。

查看联合安全性,saml令牌和sts提供者

相关问题