PasswordHash的比较是发生在客户端级别还是数据库级别
本文关键字:数据库 客户端 比较 PasswordHash | 更新日期: 2023-09-27 18:09:10
我需要澄清一些关于用户身份验证的事情,因为我想知道它是如何正确完成的,
我被告知身份验证是一个涉及安全性的过程。因此,身份验证过程应该尽可能对客户端隐藏,客户端应该只接收一个标志值(1=密码正确且已通过身份验证,-1=密码不正确)和/或已通过身份验证的用户的用户名。因此,它应该是一个应该在后端发生的SQL操作。因此,详细的过程应该是,客户端表单发送用户名和哈希/加密密码给SQL, SQL进行身份验证,并发送带有用户名的1/-1给客户端作为返回值和输出。
但是当我在线搜索时,在用户认证上,情况并非如此,我在web上看到的所有线程,无论是应用程序是windows还是web,开发人员做两个操作:
-
在SQL server中,密码的saltHash值是根据用户名搜索的,然后将saltHash值与PasswordHash一起返回给客户端表单/或业务层
-
在客户端或BLL中,使用输入的密码和返回的saltHash值生成PasswordHash,然后将此PasswordHash与返回的PasswordHash进行比较。
这实际上是开发人员在登录事件中编写代码来验证用户的方式吗?如果是这样,基本上密码比较发生在客户端(无论是在表单中还是在业务层中),那么将实际的saltHash和passwordHash暴露给客户端不会带来巨大的安全风险吗?
如果有人能给我写一些代码来演示SQL和c#的登录/认证过程,那将是一种乐趣!PS-当你回复时请不要使用LINQ, LINQ到SQL或实体框架。目前我还不认识他们。还在学习。请从Ado .NET回复
谢谢
这是一个更大的利弊讨论,很难笼统地说明。客户端散列的优点:
- 由于加密安全哈希是计算密集型的,通过将它们推送到客户端,可以减少来自服务器的负载。
- 服务器永远不会看到实际的密码,并且由于用户在站点之间共享密码,如果您的系统被泄露,您可以声称密码没有泄露。
服务器端哈希的优点:
- 计算哈希值的代码不需要为多个客户端编写,即各种移动设备,如Windows Phone、Android、IOS以及浏览器。有时,当存在兼容性差异时,还需要为浏览器编写专门的代码。
- 哈希可以更"安全",例如,您可以为每个用户设置随机盐,这使得如果数据库被破坏,某人暴力攻击密码的计算成本很高。这为您提供了更多的时间通知用户,如果发生违规行为,并为用户更改密码。
其他指南:
- 永远不要在数据库中存储密码,无论是加密的还是明文的。你不能保证每个访问数据库的人都不会看到密码,被黑客攻击,或者犯错误并危及它们。
- 这样的流量必须始终通过TLS或SSL,这将防止许多攻击(如窃听,MITM,重放等)
- 始终使用加密函数(如SHA2哈希,PBKDF2派生盐等)对所有加密操作。
以下是关于如何在客户端和服务器场景中实现密码散列的一些指导方针。
如果哈希在服务器上:
- 用户第一次注册时:
- 生成随机盐
- 使用此盐对密码进行散列
- 在DB中存储盐和密码哈希值
- 用户登录时
- 从DB中读取盐
- 计算密码哈希
- 与DB 比较
如果哈希在客户端:
- 用户第一次注册时:
- 在客户端上基于某些用户属性(如用户名)生成一个salt
- 在客户端使用salt对密码进行哈希
- 发送密码哈希给服务器,并存储DB
- 用户登录时
- 基于相同的用户属性生成盐(例如,您可以始终使用用户名)
- 在客户端使用salt对密码进行哈希
- 将密码哈希值发送给服务器,与DB 中的哈希值进行比较
该方法在客户端/服务器应用程序和web应用程序之间可能是相同的,但直接访问SQL数据库的windows应用程序是另一回事。您可能确实希望避免泄露散列密码,但盐不需要是私有的。您可以获取用户端的盐和客户端的散列,然后比较服务器端。
我不知道SQL是否有实际的密码哈希算法(mcrypt, PBKDF2等)或仅限于纯粹的加密算法,如SHA家族,但在服务器之外进行哈希的好处是您可以选择自己的哈希算法。您还可以围绕它构建一些逻辑。例如,您可以存储个人用户使用的哈希算法,然后如果将来它被破坏,您可以在下次登录时将其更新为更安全的算法。
您还必须认识到,无论您做什么,除了SQL身份验证之外,几乎没有实际的安全性。修改程序以忽略SQL服务器的响应并继续进行,就好像登录成功一样,这是相当简单的。如果需要实际的安全性,则需要在客户机和数据库之间放置一个应用服务器。反编译程序,看看它做了什么,直接或在另一个程序中使用该信息,完全绕过身份验证,也很简单。
关于重用,将身份验证分离到单独的模块中并在两个应用程序中重用该模块将是一种更好的方法。ASP。. NET有一些内置的健壮的身份验证,所以我想看看,要么使用,要么作为一个例子。Crypto.HashPassword()
已经使用了当前公认的最佳实践,只要您为每个用户提供相对较长的随机盐(例如8+字符)。
我将使用以下方法获得随机盐,但我不能在登录用户事件中使用此方法,因为即使用户输入正确的凭据,它也会返回失败的身份验证。这是因为在登录表单中输入的密码将使用随机盐进行散列,该盐与数据库中该用户的盐不同
private static byte[] GetSalt()
{
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
byte[] b = new byte[32];
rng.GetBytes(b);
return b;
}
下面是用户表结构
CREATE TABLE [dbo].[Users](
[UserId] [int] NOT NULL IDENTITY(1,1) PRIMARY KEY,
[LoginName] [varchar](256) NOT NULL UNIQUE,
[UserName] [varchar](256) NOT NULL,
[EmailAddress] [varchar](256) NULL,
[IsActive] [bit] NOT NULL,
[UserPasswordHash] [varbinary](64) NULL,
[UserPasswordSalt] [varbinary](32) NULL
)
GO