ASP.NET利用验证密码拒绝非法访问

目录:
简介
如何进行Captchas 操作
探究Captcha的代码
绘制Captcha图片
创建hash 值
简介
谁正在访问你的web程序?如果你认为仅仅是注册后登录的人,那么结果会让你大出所料的。学习如何区分正在登录的是人还是机器,如何拒绝自动的注册登陆请求是很重要的
  如果您正开发一个基于商业逻辑的客户程序,你可能希望在不同机器上执行一个事务,或者希望以不同的用户身份来结束事务。当您运行一个要求合理用户帐号的商业程序时,您可能惊讶的发现您的一些用户帐号可能属于一个人-一个人在他的机器上巧妙的运行一个脚本来在你的商业逻辑上创建不同的“虚”帐户。这些帐户耗费您的资源,带宽,和其他时间和原料。
利用脚本创建不同帐户的处理叫做身份欺骗,对于大多数的简单站点,这个操作是很容易被实现的。所有的欺骗者需要创建一个HTML窗体,这个窗体需要包含和您的登录窗体相同的字段,然后利用“HTTP-POST”传输数据到您的服务器,你的服务器就会执行相应的创建帐户操作。更糟糕的情况是如果您的登录窗体用"HTTP-GET".操作执行,一旦成功创建了一个帐户,就没有办法能够阻止欺骗者反复自动的执行整个创建过程。
利用一个自动化脚本,欺骗者可以使用一个名字逐个创建几百个帐户,如果你的服务器不能有效的验证这些数据,你将陷入处理大量无用的虚拟帐号的麻烦中,并且大量的访问可能耗费您的系统资源,使你的应用程序变慢或者崩溃。
其他的潜在欺骗问题也可能发生,比如很容易写一个脚本,利用这个脚本可以使用相同的用户帐号在不同的web浏览器中登陆访问。虽然这对于一些站点来说不算是个问题,但是当web程序允许客户端需要下载文件或者其他资源时就会浪费资源和带宽。一些应用程序在允许用户在浏览器中创建新的实例时,检测这个用户是否已经登录了。多客户端的攻击会引起服务器不停的执行登录检查操作,这样也会耗费服务器的资源例如:数据库连接和系统内存。
CAPTCHA解决方案
有很多方法可以阻止创建用户帐号的欺骗。本文主要讨论叫做"word-verification technology"(逐字验证)的技术,以及在你的程序中起到的正面和负面作用,一些流行的站点例如Yahoo和 Msn Hotmail 在它们的应用程序中也利用这个技术来减少邮件帐号域中产生垃圾邮件。Yahoo的逐字校验技术采用的是芝加哥梅隆大学开发的叫做"CAPTCHA Project"的技术。
Captcha  是一种将人和机器区分出来的方法。一个captcha方法提出一个问题,这个问题对于人来说是很容易解决的,而对于机器来说却是不可能或者很难解决的,一个通用的captcha方法是为用户提供一个包含内置文本的图片,用户必须破解这些文本,而且随着用户登录窗体或者用户帐号创建窗体一起提交。例如图1显示了一个简单的captcha 图片
 
图1显示了一个简单得例子,它包含了人类可读的一组随机的大小写混合的字符组合,。通过含有图案的背景和一些附加的线来增加非人的机器鉴别文本的难度。
人类很容易从图片1中读到“PVHKf”,但是一个机器就很难读取这些字符,对于一个机器要想成功的译解这个文本,它必须能够使用光学字符识别引擎(ocr)读取文本. 虽然ocr 引擎日益变得精确,可以很轻易的区分背景的图案和颜色,即使掺杂了线或者点。你可以利用人/机读取文本能力的不同来提供ocr 引擎很难辨认的文本。这样就可以确保一个新的访问或者登录来自一个人而不是机器
  在提供正确验证方面,captcha技术是最好最有价值的。OCR技术在破坏captcha技术方面有了更高级的进化,简单的文本迷惑技术很容易被OCR扫描出captchas,并正确读取图片中字符来进行攻击。作为响应,你可以使用制作随意的弧形,线,背景图案来使captchas更难读, 然而如果你将译解文本做的太夸张了,那可能对合法用户辨认也是一个挑战。
幸运的是,基于OCR的攻击还不是很完美和通用,所以这里讨论的逐字验证技术对于大多数的预定的攻击者提供足够的保护333
如何进行Captchas 操作
登录操作或者创建新的用户操作必须包含一个逻辑比较,应用程序将一些文本绘制到一个图片中,用户读完这些文本后,进行逻辑比较,如果这些绘制的文本和用户读取后输入的文本相等,你就可以认为这个用户是一个人而不是在一个机器上自动运行的脚本。
在服务器上进行验证匹配比较不能解决那种通过向服务器上发送大量请求的欺骗攻击,作为一个健壮的解决方案,这个应用程序应该包含下面的约束:
1. 将系统资源使用减少到最小,这个应用程序不应该包含任何种类型的数据存储――它不应该包含写文件,存储信息到数据库等等
2. 应用程序不要使用会话(session)来管理状态。会话是不可升级的解决方案,不但因为他们被存储在服务器(违反了前面所说的约束),而且他们在web窗体中使用的也不好
3. 为了保证应用程序不被多请求欺骗攻击,应用程序应该在客户端验证captcha字符和用户响应输入的字符,而不是在服务器上进行验证
为了确保这些约束的实现,很显然你应该在客户端存储这个随机产生的字符串。这个存储的数据必须经过加密,让欺骗者不能并利用脚本自动读取提交。这个 sample application提供了一个通过使用SHA1散列技术来哈希字符串的解决方案。
一个hash是一个从字符串内容中产生的值或者键,应用程序应该能够使用简短的hash值替代一个原始的字符串,散列法提供了更快的编码。你不能用hashed的值来覆盖原始的字符串,这里有好多的散列法算法,SHA1散列算法产生hash的值,例如散列一个给定的字符串总是产生相同的Hash 值,其他的字符串不能产生相同的散列值。如果你有两个字符串的Hash值,如果原始字符串的内容是相同的,为了更高的准确性你应该比较Hash 值而不是直接比较字符串本身。你可以看一些更多关于SHA1 hash 算法的文章here
既然这样,你可以在服务器上提取嵌入captcha的字符,然后发送他们到客户端保存在窗体的HTTP cookie中,接着用户将从图片中读取的文本发回到服务器,服务器将用户输入的文本和保存在HTTP cookie 中的文本分别哈希后进行比较,如果这两个哈希字符串相等,那么表示用户成功输入了嵌入在captcha中的字符。
接下来当你创建新的页面时你必须正确地决定在那里和如何存储哈希值
虽然创建一个返回包含图片和内容(content)的web窗体是可能的,但是您不能同时返回他们。为了返回一个JPEG格式的图片,这个页面必须设置Response.ContentType 为"image/jpeg,",反之如果想要返回的是HTML内容,Response.ContentType 应该设置为 "text/html".。因此最简单的办法就是使用两个页面:一个页面返回文本的内容,另一个页面创建并返回一个captcha 图片。
当服务器创建包含文本内容的页面时,它将包括<img src="randomimage.aspx">标志,浏览器能够解析Img标志,然后作为单独请求从服务器请求这个图片。这个请求将会产生一个随机的字符串和图片,但是它不能写文本内容返回给浏览器,因为这个浏览器提交的是一个图片响应。Response.ContentType必须是image/jpeg." ,因此当浏览器请求图片时你必须想办法将这个生成的Hash值返回给客户端,这样在客户端就可以验证用户输入的内容和Hash 值是否相符
因为前面所说的约束取消了Session,文件,和数据库的存储机制,所以你只剩下了cookies,在将随机字符放入生成的图片之前,生成一个Hash  并且将包含Hash 值的cookies 写入HTTP-Headers,这个Cookies 能够作为响应头的一部分发送给客户端。因为没有Http_Content 没有包含文本,所以the Response.ContentType 设置成 "image/jpeg" 将能正确提交。
现在你有了一个符合所有约束的解决方案,下面是我们钻研代码的时间了
探究Captcha的代码
这个主页面(Index.asp)包含了一些用户输入控件,一个随机的图片文件,一个提交按钮,这个页包含类似下面Html内容:
<form id="Form1" method="post" runat="server">
      <asp:image id= "Image1" runat= "server"
         ImageUrl= "DrawRandomImage.aspx">
      <asp:textbox id="AccessKey"
         runat="server"></asp:textbox>
      <asp:Label id= "lblResult" runat= "server">
      </asp:Label>
   </form>
注意一下图片控件的标志(asp:image)和标准的Html图片标志是非常类似,只是这个图片控件标志的url 指向了一个返回图片的页面而不是直接指向一些图片文件。
这个程序还有两个部分逻辑需要弄明白,首先你需要产生一个随机的字符串:下面的GenerateRandomString函数 通过一个指定随机数长度的参数,产生字符范围在a-z 和 A-Z之间随机字符串,如果你想创建一个包含希腊数字的字符串,你也可以通过修改字符串范围来实现
Public Shared Function GenerateRandomString(
      ByVal iLength As Integer) As String
      Dim iStartBC, iEndBC, iStartSC, iEndSC, _
      iCount, iTmpC As Integer
      Dim sRandomString As String
      Dim rRandom As New Random( _
         System.DateTime.Now.Millisecond)
      ' Convert characters into their integer equivalents
      ' (their ASCII values)
      iStartSC = Asc("a")
      iEndSC = Asc("z")
      iStartBC = Asc("A")
      iEndBC = Asc("Z")
      ' Now loop as many times as is necessary to build
      ' the string length we want
      While (iCount < iLength)
         ' Assign a random number between the MAX
         ' and MIN values
         iTmpC = rRandom.Next(iStartBC, iEndSC)
         If (((iTmpC >= iStartSC) And (iTmpC <= iEndSC) _
            Or (iTmpC >= iStartBC) And _
            (iTmpC <= iEndBC))) Then
            sRandomString +=  Chr(iTmpC)
            iCount + =   1
         End If
      End While
   End Function
创建hash 值
下一步,你需要为这个函数产生的随机字符串用散列算法创建一个Hash 值。然而这不能解决所有问题,一旦一个欺骗者注意到所有的页面正在将一个hash版本的字符串发回给客户端,他可以很轻易的发现攻击方法。通过比较用户输入的任何无序字符串,他可以创建一个欺骗:
1. 他任意选择一个要发送的hash字符串
2. 明文(clear text)发送他选择的字符串到服务器处理逻辑
服务器处理逻辑做的工作是:
1. 哈希(Hash)明文(clear text)
2. 比较哈希的明文(clear text)和发送过来的hash 值
所以你必须想办法阻止欺骗者简单地散列(hashing)他选择的字符串。然而 Hashes 是一个“"one-way”函数。你必须要校验hash必须是从你的应用程序中产生的。
创建一个唯一的Hash
这是一个机器验证检查(MAC)最适用的地方,就像名称暗示的那样,MAC是一种从特殊机器上产生唯一Hash 值的技术。你在哈希整个消息前你应该追加这个唯一的值到你的纯文本正文中。当你这样做以后,只有机器知道打开Hash 值的密钥,没有其他人可以知道如何产生完全相同的Hash , 即使他们知道这个纯文本的消息,。这里显示一些操作步骤:
1. 用Guid.NewGuid 函数产生一个唯一的GUID 密钥 (MAC key)
2. 在应用程序的web.config 中 保存Mac key
3. 检索存储的mac密钥,并追加它到你想哈希的字符串
4. 哈希整个消息
5. 在cookie中保存这个哈希结果
6. 为了进行验证操作,保证你的窗体利用HTTP-POST发送用户输入的用来验证的字符串到服务器
7. 当应用程序收到这个字符串时,追加web.config 中的MAC键到这个字符串的末尾
8. 哈希第七步产生的整个消息
9. 从htttp 请求头中检索这个cookie值,并且比较第八步产生的消息和第四步的cookie值
10. 如果这些值不相等,你或者收到了一个欺骗 或者用户在读写captcha文本时出现了错误
如果不知道Mac key 是什么,没有人可移植产生相同的hash 甚至相同的纯文本。这就很好的防止了Man-In-the-Middle (MITM)攻击,事实上ASP.NET的 EnableViewStateMAC命令也提供了防止MITM攻击的附加安全。这里有一些哈希函数的代码,你可以在downloadable code:中RandomStringGenerator.vb类中找到这些函数代码:
Public Shared Function HashMACMe(ByVal s As String) _
      As String
      Dim b As Byte
      Dim HashValue() As Byte
      Dim retString As String
      ' Create a new instance of the UnicodeEncoding
      ' class to convert the string into an array of
      ' Unicode bytes
      Dim UE As New UnicodeEncoding
      'Convert the string into an array of bytes.
      Dim MessageBytes As Byte() = UE.GetBytes(s & _
         AppSettings("MACKey"))
      ' Create a new instance of the SHA1Managed class
      ' to create the hash value.
      Dim SHhash As New SHA1Managed
      ' Create the hash value from the array of bytes.
      HashValue = SHhash.ComputeHash(MessageBytes)
      ' Return a hexadecimal representation of the string
      For Each b In HashValue
         retString += b.ToString("X2")
      Next
Return retString
   End Function
这些代码从web.config 中读取mac key,然后在散列最终的输出信息前追加他们到初始的字符串。这个页作为cookie 发送这个hash 值
绘制Captcha图片
你还需要在一个图片中绘制这些字符串。.net framework将绘制的过程简化为了几行简单的代码Listing 1显示了如何绘图的代码,这些你也可以在在downloadable code.里面的DrawRandomImage.aspx.vb文件中找到。
Listing 1中的代码使用了一个字符串参数(ds)然后用粗体的格式绘制到图片上,最后添加一些随机的线到图片中来防止OCR 扫描软件,最后这个页的ContentType被设置撑"image/jpeg"
然后利用Response.OutputStream保存图片。
把这些连接起来,下面就是page_load 事件的代码:
Private Sub Page_Load(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles MyBase.Load
  
      ' Conjure up some Random Characters in a String
      Dim b As RandomStringGenerator
      Dim s As String
      s = b.GenerateRandomString(5)
  
      ' Hash the Random String together with a SecretKey
      ' (Machine Authentication Check) to prevent MITM spoof
      Dim hMACIString As String = b.HashMACMe(s)
  
      ' Store the results in a HTTPCookie
      Dim c As HttpCookie = New HttpCookie("hMACIString")
      c.Value = hMACIString
      Dim dtNow As DateTime = DateTime.Now
      ' Set expiration of 365 days - Change this to your requirements
      Dim tsYear As New TimeSpan(365, 0, 0, 0)           
      c.Expires = dtNow.Add(tsYear)
      Response.Cookies.Add(c)
  
      ' Call the above DrawStringImage routine
      Call DrawStringImage(s)
   End Sub
校验数据
现在剩下的就是校验提交按钮触发事件提交的数据了:
Private Sub Button1_Click(ByVal sender As System.Object, _
      ByVal e As System.EventArgs) Handles Button1.Click
      Dim hMACIString As String
      Try
         Dim c As HttpCookie =
            Request.Cookies("hMACIString")
         hMACIString = c.Value
      Catch
      End Try
      Dim b As RandomStringGenerator
      ' Send to next page in a real application
      If (hMACIString = b.HashMACMe(AccessKey.Text)) Then
         lblResult.Text = "Real Person"< /EM >
      Else
         lblResult.Text = "SPOOFED"
      End If
   End Sub  
AccessKey控件包含了用户输入的字符串数据,客户端将它发回给服务器,在它的后面加上mac 密钥,哈希这个字符串,比较他和DrawRandomImage.aspx页面的cookie值是否相等,下面的图显示了在浏览器完整的页面
 
通过上面的代码片断,你可以发现在.net 中实现captcha技术是很简单的,你所要做就是不要被随机图片所迷惑,不要设计太复杂的captcha 以至于合法用户都不能正确读取。
还有要注意的是我是将它作为应用程序的一个附加的安全层来介绍的,他们必须和应用程序的其他安全层窗体一起工作
为了扩展这个想法,你可以研究一下将captcha作为面向服务器的Web service,这样你的其他登陆窗体就可以利用它。你可以利用WS-Attachments 和 DIME技术来尝试输出二进制图片流,需要注意的是WS-Attachments 和 DIME不支持Indigo(靛颜色),不过微软正在更新Web services 框架。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值