"可变"登录
具有泄露冗余处理的登录系统。
引言
我是在看了电影《虎胆龙威4》后产生了这个概念的,电影中那位白帽黑客写了他称之为“变异安全算法”的东西。我想,如果一个系统在登录失败次数过多而可能存在安全风险时能改变(或变异)密码,那可能会很有用。
这个登录系统与你常规的系统不同。传统的登录系统依赖于一个固定的用户名和密码组合来允许用户访问私人信息。许多人在注册成为社区网站的成员时,或者在设置网上银行系统的登录信息时,并不经常使用足够强的密码。
这些密码通常是注册者出生日期或有意义的名字。这就是安全风险所在。
一个可能的场景是,一个女人在 Facebook 上注册,并将密码设置为她女儿的全名。她在 Facebook 上的朋友,如果“调皮”起来,可能会猜测她的密码与她女儿有关,并尝试猜测她的登录详细信息。
在这种情况下,传统的登录系统将允许无限次的猜测,期间“攻击者”最终可能会猜对并获得访问权限。
这个登录系统解决了这种情况,并为注册用户提供了更多安全保障。
背景
本系统是用 Visual Basic.NET 编写的,并使用 SQL Server 数据库来存储用户信息。大部分用户数据以纯文本形式存储,除了用户的密码,它使用 SHA1 进行加密。
开发过程中的挑战包括用户密码加密以及通过会话变量跟踪登录尝试(每次尝试都会计数)。这些问题在概念上比较小,并且相对较快地得到了解决。
工作原理
注册时,新用户将收到一封电子邮件,其中包含其账户的详细信息。这些详细信息包括他们的用户 ID、用户名和密码。
要登录,用户首先需要输入他们的用户 ID,以验证他们确实在该系统上拥有账户。一旦用户 ID 得到验证,他们将被要求提供与他们输入的 User ID 关联的用户名和密码。
当当前会话记录的登录尝试次数少于 3 次时,系统会对登录表单密码字段中输入的值进行哈希和加盐处理,并从数据库中选择用户 ID、用户名和密码值与用户输入相同的记录。如果此选择没有返回单行记录,则表示登录无效,并且会话中会记录一次尝试。
如果登录尝试次数达到 3 次,该账户将被标记为锁定,并且在用户恢复该账户之前,将不会包含在后续登录尝试的数据库选择中。
此外,在账户锁定后,将生成一个 36 个字符长的值。该值随后会被哈希和加盐处理,并存储在数据库中作为账户的新密码。
Login.aspx.vb
Imports System.Data
Imports System.Data.SqlClient
Public Class Login
Inherits System.Web.UI.Page
Private connstr As String = "Data Source=.\SQLEXPRESS; Initial Catalog=test; Integrated Security=True;"
Private conn As SqlConnection = New SqlConnection(connstr)
Public NewPass As String = Nothing
Public HashedPass As String = Nothing
Public UserId As Integer = Nothing
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
pnlLoginForm.Visible = False
Session("LoginAttempt") = 0
End If
If Session("LoginAttempt") = 2 Then
' This account may be vulnerable, generate a random string for the new password and lock the account
Dim s As String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Dim r As New Random
Dim sb As New StringBuilder
Dim idx As Integer = Nothing
For i As Integer = 1 To 8
idx = r.Next(0, 61)
NewPass += sb.Append(s.Substring(idx, 1)).ToString
Next
HashedPass = Crypt.Compute(NewPass, "SHA512", Nothing)
Dim sql As String = "UPDATE Users SET Password = @Pass, LockedYN = @Lock"
Dim cmd As New SqlCommand(sql, conn)
cmd.Parameters.AddWithValue("@Pass", NewPass)
cmd.Parameters.AddWithValue("@Lock", "Yes")
conn.Open()
cmd.ExecuteNonQuery()
conn.Close()
End If
End Sub
Private Sub btnVerifyID_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnVerifyID.Click
If txtUserId.Text IsNot Nothing Then
Dim UserData As New DataSet
Dim UserAdapter As New SqlDataAdapter
UserAdapter.SelectCommand = New SqlCommand("SELECT * FROM Users " & _
"WHERE UserId = @ID", conn)
UserAdapter.SelectCommand.Parameters.AddWithValue("@ID", txtUserId.Text)
UserAdapter.Fill(UserData)
If UserData.Tables(0).Rows.Count <> 1 Then
lblError.Text = "Specified User ID does not exist."
lblError.ForeColor = Drawing.Color.Red
Else
If UserData.Tables(0).Rows(0)(4).ToString = "Yes" Then
lblError.Text = "The account you tried to log into has been locked to prevent possible unauthorized access. " & _
"If this is your account, please check your email for instructions to unlock it."
lblError.ForeColor = Drawing.Color.Red
Else
UserId = txtUserId.Text
pnlLoginForm.Visible = True
End If
End If
End If
End Sub
Private Sub btnLogin_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnLogin.Click
If txtUser.Text IsNot Nothing And txtPass.Text IsNot Nothing Then
If Session("LoginAttempt") <> 2 Then
Dim pass As String = Crypt.Compute(txtPass.Text, "SHA512", Encoding.UTF8.GetBytes(Crypt.SaltValue))
Dim UserData As New DataSet
Dim UserAdapter As New SqlDataAdapter
UserAdapter.SelectCommand = _
New SqlCommand("SELECT * FROM Users " & _
"WHERE Username = @User AND Password = @Pass " & _
"AND LockedYN = @Lock", conn)
UserAdapter.SelectCommand.Parameters.AddWithValue("@User", txtUser.Text)
UserAdapter.SelectCommand.Parameters.AddWithValue("@Pass", pass)
UserAdapter.SelectCommand.Parameters.AddWithValue("@Lock", "No")
UserAdapter.Fill(UserData)
If UserData.Tables(0).Rows.Count <> 1 Then
lblError.Text = "Invalid username or password."
lblError.ForeColor = Drawing.Color.Red
Session("LoginAttempt") = CInt(Session("LoginAttempt")) + 1
Else
Session("LoggedIn") = True
Response.Redirect("Home.aspx")
End If
Else
lblError.Text = "The account you tried to log into is now locked due to excessive failed login attempts. " & _
"If this is your account, please check your email for instructions to unlock it."
lblError.ForeColor = Drawing.Color.Red
End If
Else
lblError.Text = "Please enter a username and password."
lblError.ForeColor = Drawing.Color.Red
End If
End Sub
End Class
本次发布包含的内容
本次发布包含基本的安全“措施”。由于时间有限,我还没有添加处理电子邮件的代码。
目前包含
- 处理用户密码 SHA1 加密的函数
- 通用的会话管理,用于跟踪登录尝试次数
- 测试时使用的最简用户界面。此版本未经清理。
未来的计划
我总是喜欢找到能在多个平台上运行的软件,所以我会着手开发一个使用 mysql 作为数据后端的 PHP 版本。
为了消除任何安全担忧,下一个版本将包含在用户已登录其账户的情况下,将用户重定向出登录页面的功能。
可能最关键的改变是,将用户 ID 的字段从整数类型修改为 guid()。这将消除用户 ID 被猜测的可能性,并使“欺骗”另一个用户账户变得更加复杂。
使用代码
在使用此系统之前,您需要更改连接字符串。目前,每个处理数据源的文件中都有一个连接字符串。
- Create.aspx.vb
- Login.aspx.vb
' This is what the connection string currently looks like:
Private connstr As String = "Data Source=.\SQLEXPRESS; Initial Catalog=test; Integrated Security=True;"
' The values for Initial Catalog and Data Source will need to be changed in order for the system to work correctly with your database.
该系统正常运行只需要一个表。为了使系统正常工作,该表必须包含以下结构。
Users 表UserID (主键, int, not null) -- identity(1,1)
Username (varchar(20), not null)
Password (varchar(36), not null)
EmailAddress (varchar(100), not null)
LockedYN (char(3), not null) -- default 'No'
这是一个 CREATE 示例,供您参考:
CREATE TABLE [dbo].[Users](
[UserID] [int] IDENTITY(1,1) NOT NULL,
[Username] [varchar](20) NOT NULL,
[Password] [varchar](36) NOT NULL,
[EmailAddress] [varchar](100) NOT NULL,
[LockedYN] [char](3) NOT NULL,
CONSTRAINT [PK_Users] PRIMARY KEY CLUSTERED
(
[UserID] ASC
)
如果您愿意,可以随意向您的用户表中添加更多列,但如果您删除或更改了其中任何一个,系统**将会**损坏。
关注点
Google 是你的朋友。加密对我来说尤其具有挑战性。我找到了很多我参考并在此基础上(Crypt.aspx.vb)编写代码的示例。