ASP.NET 的 CAPTCHA 控件






4.84/5 (158投票s)
2004 年 11 月 7 日
7分钟阅读

2185350

32589
一个作为一个简单的、可视化的拖放式 ASP.NET 服务器控件实现的 CAPTCHA 控件。
引言
我相信在座的各位都对垃圾邮件有所了解。目前存在两种思想流派来对抗垃圾邮件:
- 如 POPFile 这样的贝叶斯过滤。
- 如 SpamArrest 这样的挑战/响应人工验证。
当然,这两种方法都有其优缺点。本文将只讨论第二种技术:验证您接收到的数据确实来自一个真实的人类,而不是机器人或脚本。CAPTCHA 是一种测试输入以确保您正在与人类打交道的方法。现在,有很多方法可以构建 CAPTCHA,正如 MSDN 上这篇关于该主题的文章所述,但我将专注于视觉数据输入 CAPTCHA。
CodeProject 上已经有一篇关于 优秀的 ASP.NET CAPTCHA 控件的文章,所以您可能想知道本文的用途。我想重新构建该解决方案,原因如下:
- 更多的控件设置和灵活性
- 转换为我喜欢的 VB.NET 语言
- 封装成一个完整的 **ASP.NET 服务器控件**。
因此,本文将介绍如何将一组现有的 ASP.NET 网页转换成一个简单的、拖放式的 ASP.NET 服务器控件——在此过程中进行了许多重要的增强。
实现
我首先要处理的是 CAPTCHA 类生成的图像。这最初是通过一个专用的 .aspx 窗体完成的——服务器控件不会有这个。我如何即时生成图像?经过一些研究,我接触到了 HttpModules 和 HttpHandlers 的世界。它们非常强大——而且一个 HttpHandler
可以很好地解决这个问题。
我们只需要在 <system.web>
部分对 Web.config 进行一个小修改。
<httpHandlers>
<add verb="GET" path="CaptchaImage.aspx"
type="WebControlCaptcha.CaptchaImageHandler, WebControlCaptcha" />
</httpHandlers>
此处理程序定义了一个名为 CaptchaImage.aspx 的特殊页面。现在,这个“页面”实际上并不存在。当请求 CaptchaImage.aspx 时,它将被实现 IHttpHandler
接口的类:CaptchaImageHandler
拦截并处理。这是相关的代码部分:
Public Sub ProcessRequest(ByVal context As System.Web.HttpContext) _
Implements System.Web.IHttpHandler.ProcessRequest
Dim app As HttpApplication = context.ApplicationInstance
'-- get the unique GUID of the captcha;
' this must be passed in via querystring
Dim strGuid As String = Convert.ToString(app.Request.QueryString("guid"))
Dim ci As CaptchaImage
If strGuid = "" Then
'-- mostly for display purposes when in design mode
'-- builds a CAPTCHA image with all default settings
'-- (this won't reflect any design time changes)
ci = New CaptchaImage
Else
'-- get the CAPTCHA from the ASP.NET cache by GUID
ci = CType(app.Context.Cache(strGuid), CaptchaImage)
app.Context.Cache.Remove(strGuid)
End If
'-- write the image to the HTTP output stream as an array of bytes
ci.Image.Save(app.Context.Response.OutputStream, _
Drawing.Imaging.ImageFormat.Jpeg)
'-- let the browser know we are sending an image,
'-- and that things are 200 A-OK
app.Response.ContentType = "image/jpeg"
app.Response.StatusCode = 200
app.Response.End()
End Sub
将生成一个新的 CAPTCHA 图像,并将图像直接从内存流式传输到浏览器。问题解决了!
但是,还有另一个问题。负责显示图像的 HttpHandler
和托管该控件的网页之间必须进行通信——否则,调用控件如何知道随机生成的 CAPTCHA 文本是什么?如果您查看渲染控件的源代码,您会看到一个 GUID 通过查询字符串传递。
<img src="CaptchaImage.aspx?guid=99fecb18-ba00-4b60-9783-37225179a704"
border='0'>
此 GUID(全局唯一标识符)是用于访问最初由控件存储在 ASP.NET 缓存中的 CAPTCHA 对象的一个键。请看 CaptchaControl.GenerateNewCaptcha
方法:
Private Sub GenerateNewCaptcha()
LocalGuid = Guid.NewGuid.ToString
If Not IsDesignMode Then
HttpContext.Current.Cache.Add(LocalGuid, _captcha, Nothing, _
DateTime.Now.AddSeconds(HttpContext.Current.Session.Timeout), _
TimeSpan.Zero, Caching.CacheItemPriority.NotRemovable, Nothing)
End If
Me.CaptchaText = _captcha.Text
Me.GeneratedAt = Now
End Sub
这可能看起来有点奇怪,但效果很好!ASP.NET 事件的顺序如下:
- 页面被渲染。
- 页面调用
CaptchaControl1.OnPreRender
。这会生成一个新的 GUID 和一个新的反映控件属性的 CAPTCHA 对象。生成的 CAPTCHA 对象按 GUID 存储在缓存中。 - 页面调用
CaptchaControl1.Render
;特殊的<img>
标记 URL 被写入浏览器。 - 浏览器尝试检索特殊的
<img>
标记 URL。 CaptchaImageHandler.ProcessRequest
被触发。它从查询字符串中检索 GUID,从缓存中检索 CAPTCHA 对象,并渲染 CAPTCHA 图像。然后它移除缓存对象。
请注意,最后还有一个小的清理工作。如果出于某种原因,控件已渲染但图像 URL 从未被检索,则缓存中将存在一个孤立的 CAPTCHA 对象。这种情况可能发生,但在实际操作中应该很少见——而且我们的缓存条目只有 20 分钟的有效期。
我早期犯的一个错误是将实际的 CAPTCHA 文本存储在 ViewState 中。ViewState 未加密,很容易被 解码! 我已将 GUID 切换为 ControlState,这对于从缓存中检索共享的 Captcha 控件至关重要——但它本身毫无用处。
CaptchaControl 属性
CaptchaControl
是一个优秀的 ASP.NET 组件,并**正确实现了所有默认的 ASP.NET 服务器控件属性**。它还有一些自己的属性:
属性 | 默认值 | 描述 |
CacheStrategy |
HttpRuntime |
出于安全原因,CAPTCHA 文本永远不会发送到客户端;它只存储在服务器上。它可以存储在 Session (对 Web 场友好)或 HttpRuntime (非常快,但仅限于一个 Web 服务器)中。 |
CaptchaBackgroundNoise |
低功耗 |
添加到 CAPTCHA 图像的背景噪点量。范围从 None 到 Extreme 。 |
CaptchaChars |
A-Z, 1-9 | 生成 CAPTCHA 文本时使用的字符白名单。将从该字符串中随机选择一个字符。默认情况下,我省略了一些容易混淆的字符,例如 O、0、I、1、8、B 等。 |
CaptchaFont |
"" | 用于 CAPTCHA 文本的字体系列。如果未提供,将为每个字符选择一个随机安装的字体。内部维护一个字体白名单,因此只使用已知的可读字体(例如,不是 WingDings)。 |
CaptchaFontWarping |
低功耗 |
用于 CAPTCHA 文本每个字符的变形级别。范围从 None 到 Extreme 。 |
CaptchaHeight |
50 | CAPTCHA 图像的默认高度(以像素为单位)。 |
CaptchaLength |
5 | 随机生成的 CAPTCHA 文本中使用的字符数。 |
CaptchaLineNoise |
无 |
添加到 CAPTCHA 图像的“涂鸦”线噪点量。范围从 None 到 Extreme 。 |
CaptchaMaxTimeout |
90 | CAPTCHA 生成后保持有效并存储在缓存中的秒数。 |
CaptchaMinTimeout |
3 | 用户在输入 CAPTCHA 之前必须等待的最小秒数。 |
CaptchaWidth |
180 | CAPTCHA 图像的默认宽度(以像素为单位)。 |
UserValidated |
假 |
回发后,如果用户输入的文本与随机生成的 CAPTCHA 文本匹配,则返回 True 。请注意,标准的 IValidation 接口也已实现。 |
LayoutStyle |
Horizontal | 确定文本和输入框是位于图像的右侧还是下方。允许更大的布局灵活性。 |
这些属性中的许多都涉及到人类可读性与机器可读性之间的固有权衡。**CAPTCHA 对于 OCR 软件来说越难读取,对我们人类来说也会越难!** 作为说明,请比较这两个 CAPTCHA 图像:
左侧的 CAPTCHA 使用所有“中等”设置生成,在人类可读性和 OCR 机器可读性之间取得了合理的权衡。右侧的 CAPTCHA 使用较低的 CaptchaFontWarping
和较小的 CaptchaLength
。如果通过 OCR 脚本来对抗 CAPTCHA 的风险较低,我强烈建议您使用更易于阅读的 CAPTCHA 设置。请记住,仅仅拥有 CAPTCHA 就可以将门槛提高很多。
CaptchaTimeout
属性是后来添加的,以缓解对 CAPTCHA 农场的担忧。可以通过重新显示 CAPTCHA 并向用户提供免费 MP3 或色情内容访问权限来“付费”给人类来解决收集到的 CAPTCHA。但是,这种技术需要时间,并且在 CAPTCHA 有时间限制的情况下无效。
结论
非常感谢 BrainJar 创建了他简单而有效的 CAPTCHA 图像类。现在我已经将其封装成一个 ASP.NET 服务器控件,您应该可以轻松地将其添加到 Web 窗体中,设置几个属性,然后开始以垃圾邮件发送者自己的方式击败他们了!
在文章顶部的演示解决方案中有更多细节和注释,请查看。请不要犹豫提供反馈,无论是好是坏!希望您喜欢这篇文章。如果您喜欢,您可能还会喜欢 我的其他文章。
历史
- 2004 年 11 月 8 日,星期一 - 发布。
- 2004 年 12 月 17 日,星期五 - 版本 1.1
- 添加了
UserValidationEvent
- 更改了默认值,使其不那么激进(更用户友好)
- 添加了
LayoutStyle
属性,可选择水平或垂直布局 - 将随机字体方法从黑名单改为白名单
- 纠正了 Robert Sindall 报告的间歇性检索顺序错误
- 转换为 VB.NET 2005 兼容 XML 注释
- 添加了
- 2006 年 10 月 29 日,星期日 - 版本 2.0
- .NET 2.0 的主要重写
- 删除了对
Session
的依赖 - 删除了对
ViewState
的依赖 - 使用
ControlState
存储 GUID - 实现了标准的
IValidator
- 完全重写了渲染器,以实现更安全的 CAPTCHA
- 添加了更多可调整的属性
- 切换到
HttpRuntime
缓存 - 将缓存优先级更改为
Caching.CacheItemPriority.NotRemovable
(这是一个错误,在旧版本和新版本中都已修复)
- 2007 年 1 月 29 日,星期一 - 版本 2.1
- 修正了长度错误
- 修正了缓存错误(单位设置为分钟,而不是秒!)
- 添加了在 Web 场中存储 CAPTCHA 文本的选项
- 添加了最小时间限制以防止激进的机器人
- 改进了响应消息,精确显示 CAPTCHA 被拒绝的原因(超时、输入错误、太快)