给 ASP.Net 表单增加验证码
一、 验证码
对于一个预防攻击的 web 表单来讲,验证码通常是一个常见的措施。因为如果对于一些 public 区域的页面内容来讲,譬如一个登录表单,如果没有必要的安全措施,很可能遭到模拟登录的暴力破解攻击,要么轻易获得特定账户的登录信息,要么给服务器增加了大量的负荷,影响正常的服务。解决的办法,一般就是在登录前给出一个随机的信息(验证码),显示在页面上,让用户填写,以确保用户是通过 web 页面来进行正常的登入,对于非法的非 web 途径登录者会看不到这个验证码从而拒绝其登录。虽然这样,往往很多攻击者会截获登录 web 页,从而也搜索出验证码,这样,验证保护措施也失去意义,一般情况下,我们可以通过将验证信息作为图像信息显示在 web 上,这样就既可以不阻碍合法用户登录,又使非法攻击者无法通过 html 搜索获得验证信息。这大抵上就是验证码的用途和意义了。
二、 ASP.Net 的验证码实现
一般传统的验证码图像一般采用一些 CGI 、 ISAPI 程序加上一些加密代码来动态生成图像, ASP 大多采用 COM 组件实现,相当辛苦。
ASP.Net 中欲实现动态验证码却相当容易,笔者大致的思路:
1、 了安全起见,一般存在于 CGI 程序的 url 中的验证码加密串最好不要出现在 html 表单中,而是采用 session 变量存储,这样验证码的校验会很容易。
2、 采用一个单独的 aspx 页面专门产生动态程序,要显示的图形验证码信息存在于 session 中,而一个系统中有可能存在多个表单,为满足整个系统要求可以在 aspx 后加一个确定的 session key 的名称,例如
1<img src="”http://xxxx/Genimg/viewImg.aspx?sessionKeyName=abc”"/>
此处的 abc 就是登录页在第一次输出表单给客户端自动生成一个随机字符串存储在 session 中的 key 名称,在服务器端脚本中可以通过 session(“abc”) 获得生成的字符串(验证码)到底是多少,通过和用户在表单的验证码输入框中输入的内容比较来确定用户是否通过正常的 ie 浏览器来访问表单。
3、 在表单的第一次显示 (get 方法 ) 时,生成一个随机数字符串,存入 session(“abc”) 中,同时将 abc 作为 sessionKeyName 的值加入到验证码图形显示生成程序 viewImg.aspx 的 url 串中。
4、 ViewImg.aspx 分析 sessionKeyName, 获取 session(“abc”) 的具体值,利用 GDI+ 产生内存图像,然后修改 http header, 按照 content-type=images/png 的格式输出二进制流,这样客户的浏览器会显示出一幅图像,图像表达的内容就是验证码。
5、 当用户填入验证码后,提交到表单验证程序,首先考察其中的验证码输入字段,发现不匹配 session(“abc”) 马上拒绝,甚至可以累计失败登录次数,乃至拒绝此 IP 连接,保护系统;匹配 session 中的存储值,可以进行进一步的其他处理(譬如登录处理,文章发表等),当然也注意销毁此 session 变量(如果以后不需要)。
6、 不同的表单,可以分配不同的 session 变量名,这样一个 ViewImg.aspx 可以为系统多个表单服务。
三、 实例解说
重点列出 viewImg.aspx 吧,具体看清单:
Imports System.IO
Public Class viewImg
Inherits System.Web.UI.Page
Private Sub Page_Load( ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase .Load
Dim img As Bitmap
Dim gdiobj As Graphics
Dim ms As MemoryStream '--内存流,存放动态图形内存印象
Dim vfycode As String '--验证码
Dim SessionKeyname As String
If (Request("SessionKeyName") <> "") Then
SessionKeyname = Request("SessionKeyName")
If (Session(SessionKeyname) <> "") Then
vfycode = Session(SessionKeyname)
Else
vfycode = ""
End If
img = New Bitmap(32, 16) '--这个宽高可以根据需要确定
gdiobj = Graphics.FromImage(img)
gdiobj.DrawString(vfycode, ( New Font("Arial", 9)), ( New SolidBrush(Color.Black)), 0, 0)
ms = New MemoryStream()
img.Save(ms, System.Drawing.Imaging.ImageFormat.Png) '--选择透明格式
Response.ClearContent() '--原本是准备输出html流,现在输出图信数据,所以要修改http头
Response.ContentType = "image/png"
Response.BinaryWrite(ms.ToArray())
Else
End If
Response.End() '--这个最好带上
End Sub
End Class