65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 的 CAPTCHA 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (158投票s)

2004 年 11 月 7 日

7分钟阅读

viewsIcon

2185350

downloadIcon

32589

一个作为一个简单的、可视化的拖放式 ASP.NET 服务器控件实现的 CAPTCHA 控件。

A CAPTCHA control implemented as a simple, drag-and-drop ASP.NET Server Control

引言

我相信在座的各位都对垃圾邮件有所了解。目前存在两种思想流派来对抗垃圾邮件:

  1. POPFile 这样的贝叶斯过滤。
  2. 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 事件的顺序如下:

  1. 页面被渲染。
  2. 页面调用 CaptchaControl1.OnPreRender。这会生成一个新的 GUID 和一个新的反映控件属性的 CAPTCHA 对象。生成的 CAPTCHA 对象按 GUID 存储在缓存中。
  3. 页面调用 CaptchaControl1.Render;特殊的 <img> 标记 URL 被写入浏览器。
  4. 浏览器尝试检索特殊的 <img> 标记 URL。
  5. CaptchaImageHandler.ProcessRequest 被触发。它从查询字符串中检索 GUID,从缓存中检索 CAPTCHA 对象,并渲染 CAPTCHA 图像。然后它移除缓存对象。

请注意,最后还有一个小的清理工作。如果出于某种原因,控件已渲染但图像 URL 从未被检索,则缓存中将存在一个孤立的 CAPTCHA 对象。这种情况可能发生,但在实际操作中应该很少见——而且我们的缓存条目只有 20 分钟的有效期。

我早期犯的一个错误是将实际的 CAPTCHA 文本存储在 ViewState 中。ViewState 未加密,很容易被 解码! 我已将 GUID 切换为 ControlState,这对于从缓存中检索共享的 Captcha 控件至关重要——但它本身毫无用处。

CaptchaControl 属性

CaptchaControl 是一个优秀的 ASP.NET 组件,并**正确实现了所有默认的 ASP.NET 服务器控件属性**。它还有一些自己的属性:

CAPTCHA control properties

属性 默认值 描述
CacheStrategy HttpRuntime 出于安全原因,CAPTCHA 文本永远不会发送到客户端;它只存储在服务器上。它可以存储在 Session(对 Web 场友好)或 HttpRuntime(非常快,但仅限于一个 Web 服务器)中。
CaptchaBackgroundNoise 低功耗 添加到 CAPTCHA 图像的背景噪点量。范围从 NoneExtreme
CaptchaChars A-Z, 1-9 生成 CAPTCHA 文本时使用的字符白名单。将从该字符串中随机选择一个字符。默认情况下,我省略了一些容易混淆的字符,例如 O、0、I、1、8、B 等。
CaptchaFont "" 用于 CAPTCHA 文本的字体系列。如果未提供,将为每个字符选择一个随机安装的字体。内部维护一个字体白名单,因此只使用已知的可读字体(例如,不是 WingDings)。
CaptchaFontWarping 低功耗 用于 CAPTCHA 文本每个字符的变形级别。范围从 NoneExtreme
CaptchaHeight 50 CAPTCHA 图像的默认高度(以像素为单位)。
CaptchaLength 5 随机生成的 CAPTCHA 文本中使用的字符数。
CaptchaLineNoise 添加到 CAPTCHA 图像的“涂鸦”线噪点量。范围从 NoneExtreme
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
    1. 添加了 UserValidationEvent
    2. 更改了默认值,使其不那么激进(更用户友好)
    3. 添加了 LayoutStyle 属性,可选择水平或垂直布局
    4. 将随机字体方法从黑名单改为白名单
    5. 纠正了 Robert Sindall 报告的间歇性检索顺序错误
    6. 转换为 VB.NET 2005 兼容 XML 注释
  • 2006 年 10 月 29 日,星期日 - 版本 2.0
    1. .NET 2.0 的主要重写
    2. 删除了对 Session 的依赖
    3. 删除了对 ViewState 的依赖
    4. 使用 ControlState 存储 GUID
    5. 实现了标准的 IValidator
    6. 完全重写了渲染器,以实现更安全的 CAPTCHA
    7. 添加了更多可调整的属性
    8. 切换到 HttpRuntime 缓存
    9. 将缓存优先级更改为 Caching.CacheItemPriority.NotRemovable(这是一个错误,在旧版本和新版本中都已修复)
  • 2007 年 1 月 29 日,星期一 - 版本 2.1
    1. 修正了长度错误
    2. 修正了缓存错误(单位设置为分钟,而不是秒!)
    3. 添加了在 Web 场中存储 CAPTCHA 文本的选项
    4. 添加了最小时间限制以防止激进的机器人
    5. 改进了响应消息,精确显示 CAPTCHA 被拒绝的原因(超时、输入错误、太快)
© . All rights reserved.