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

ASP.NET 控件可防止机器人自动注册

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.80/5 (29投票s)

2002年11月8日

6分钟阅读

viewsIcon

351993

downloadIcon

4143

一组控件,当放置在网页表单上时,通过强制用户输入图片中显示的安全码来帮助防止注册页面被滥用。

引言

AntiAuto 是一组控件,可用于 ASP.NET Web 表单,以防止“机器人”自动注册。这是最近在 Lounge 中提到的,我认为它将成为 ASP.NET 卓越的应用程序架构的一个很好的例子。

许多网站要求用户注册才能获得完全访问权限。例如,Hotmail.com 要求您输入一些基本信息才能为您提供电子邮件地址。然而,这种服务容易被滥用——通过自动完成注册过程并生成随机用户名等的软件自动生成大量电子邮件地址用于发送垃圾邮件。

为了防止这种滥用,许多网站要求用户输入图片中显示的代码——从而强制用户手动输入注册详情。

目标是开发一组易于部署且能适应现有注册流程的 ASP.NET 控件。

解决方案概览

为了概述项目中的类,这里有一个快速的 UML 类图。

重要的类是 CodeImageCodeImageValidatorUtil 是一个支持类,提供其他各种部分所需的加密和解密功能。

CodeImage

此类的目的是在网页表单上生成显示图像的 HTML。图像本身由 DrawImage.aspx 文件生成,该文件又使用 PictureGenerator 类。

CodeImageValidator

这是一个派生自 BaseValidator 的类,用于验证关联文本框 (ControlToValidate) 的内容是否与 CodeImage 控件 (CodeImageControl) 中绘制的内容匹配。

它们如何协同工作

当页面首次加载时,会生成一个随机数,然后将其作为查询字符串的一部分传递给 DrawImage.aspx 页面。此数字在所有回发期间都保留在 ViewState 中。在回发期间,验证控件 (CodeImageValidator) 用于确保表单可以成功验证。

实现细节

CodeImage

CodeImageCodeImage.cs 文件中实现。该控件的目的是输出必要的 HTML 代码以绘制图像。例如,如果 DrawImage.aspx 页面要绘制图片,则 HTML 输出将如下所示

<img src="DrawImage.aspx?code=12345">

整个类的代码如下

public class CodeImage : Control
{
    private string _key;
    private int _digits;
    public CodeImage()
    {
        _key = System.Configuration.ConfigurationSettings.AppSettings["EncryptionKey"];
        _digits = Int32.Parse(System.Configuration.ConfigurationSettings.AppSettings["Digits"]);
    }

    public string Code
    {
        get
        {
            return (string)ViewState["SecretCode"];
        }
        set
        {
            ViewState["SecretCode"] = value;
        }
    }

    protected override void Render(HtmlTextWriter output)
    {
        output.Write( String.Format("<img src=\"DrawImage.aspx?code={0}\">", 
                        HttpUtility.UrlEncode(Code)));
    }

    protected override void OnLoad(System.EventArgs e)
    {
        if (!this.Page.IsPostBack)
        {
            StringBuilder sbCode = new StringBuilder(_digits,_digits);

            Random R = new Random();
            int i;
            int MaxLimit = 9;
            for(i = 0; i < _digits; i++)
            {
                sbCode.Append(R.Next(MaxLimit));
            }
            Code = Util.EncryptString(sbCode.ToString(),_key);
        }
    }
}

代码非常简单,在构造函数中加载了一些配置设置。这些设置在各种控件和页面中通用,因此使用 web.config 配置文件是最好的方式。

Render 方法被重写以输出 HTML 代码,图片中要显示的安全码来自 Code 属性。由于 HTML 代码对用户可见,因此有必要在将其放入 QueryString 之前对数字进行加密,因此,不是使用 *code=12345*,而是使用 *code=jA89AlxmmA* 等。此加密由 Util 类中的 EncryptString 方法执行。当 PictureGenerator 类被要求渲染图像时,代码会被解密。

同样重要的是要注意,加密后的安全码会首先进行 UrlEncode 处理,以确保在将其放入 QueryString 之前,任何无效字符都能得到安全处理。然后,它们会在另一端(在 DrawImage.aspx 中)进行 UrlDecode 处理。

如果页面是首次加载(即不在回发中),则 OnLoad 事件用于生成一个随机数。一旦生成了安全码,它就会存储在 Code 属性中,该属性又将其存储在 ViewState 中(从而允许内容在回发之间持久化)。

CodeImage 控件将直接放入 ASP.NET Web 表单中,其代码将在本文后面介绍。

CodeImageValidator

该类在 CodeImageValidator.cs 文件中实现,用于检查关联文本框的内容是否与 CodeImage 控件生成的代码匹配。

同样,代码相对简单,因此在讨论之前,这里是整个类的实现

public class CodeImageValidator : BaseValidator
{
    TextBox _codeTextBox;
    string _codeImageId;
    CodeImage _codeImageControl;
    string _key;


    public CodeImageValidator()
    {
        _key = System.Configuration.ConfigurationSettings.AppSettings["EncryptionKey"];
    }

    protected override bool ControlPropertiesValid()
    {
        // Should have a text box control to check
        Control ctrl = FindControl(ControlToValidate);
        Control imageControl = FindControl(_codeImageId);

        if ( (null != ctrl) && (null != imageControl) )
        {
            if ((ctrl is System.Web.UI.WebControls.TextBox) &&
                (imageControl is CodeImage))
            {
                _codeTextBox = (System.Web.UI.WebControls.TextBox) ctrl;
                _codeImageControl = (CodeImage) imageControl;

                return ( (null != _codeTextBox) && (null != _codeImageControl) );
            } 
            else
                return false;
        }
        else
            return false;
    }

    public string CodeImageControl
    {
        get
        {
            return _codeImageId;
        }
        set
        {
            _codeImageId = value;
        }
    }

    protected override bool EvaluateIsValid()
    {
        return (Util.DecryptString(_codeImageControl.Code,_key) == 
              _codeTextBox.Text);
    }
}

CodeImageValidator 派生自 BaseValidator,因此它应该像任何其他验证控件一样运行。它需要重写在提交表单时将调用的 EvaluateIsValid 方法。

ControlPropertiesValid 方法用于获取验证期间使用的控件的引用——TextBoxCodeImage 控件。这些引用然后保存在 ctrlimageControl 字段中。CodeImageValidator 控件的两个属性用于在 ASPX 页面中设置它们,分别是 ControlToValidateCodeImageControl

CodeImage 控件有一个属性 (Code),其中包含要输入到文本框中的加密安全码。然后,在 EvaluateIsValid 方法中解密该安全码,并将其与文本框的内容进行比较。如果它们匹配,则验证成功,并且该方法可以返回 true。

DrawImage.aspx

DrawImage 页面用于生成发送到浏览器的 JPEG 流,并使用 PictureGenerator 类。DrawImage 页面的代码如下

<%@ Page Language="C#" ContentType="image/jpeg" %>
<script language="C#" runat="server">
    void Page_Load (Object sender, EventArgs e)
    {
        // Draw the output
        Etier.AntiAuto.PictureGenerator.OutputPicture( this, 
                HttpUtility.UrlDecode(Request.QueryString["code"]) );
    }
</script>

首先,ContentType 被设置为 image/jpeg(确保浏览器将内容渲染为图像,而不是 HTML 页面或任何其他类型)。调用 OutputPicture 方法,传入当前页面和要渲染的加密安全码的引用。

OutputPicture 方法的代码如下

public static void OutputPicture(System.Web.UI.Page page, string encryptedCode)
{
    page.Response.Clear();
            
    string key = System.Configuration.ConfigurationSettings.AppSettings["EncryptionKey"];
    int digits = Int32.Parse(System.Configuration.ConfigurationSettings.AppSettings["Digits"]);

    Bitmap codeBitmap = new Bitmap((digits*16)+20,26, PixelFormat.Format24bppRgb);
    Graphics g = Graphics.FromImage(codeBitmap);    

    g.SmoothingMode = SmoothingMode.AntiAlias;
    g.Clear(Color.Orange);

    g.DrawString( Util.DecryptString(encryptedCode,key), new Font("Arial", 16,
                  FontStyle.Bold ), SystemBrushes.WindowText, new Point(10,2));

    codeBitmap.Save( page.Response.OutputStream, ImageFormat.Jpeg);
            
    g.Dispose();
    codeBitmap.Dispose();
    page.Response.End();
}

该代码基于 Nick Parker 在 CodeProject 上的文章“ASP.NET 中的实时 Web 图形”。它加载配置设置,然后渲染图像,首先解密安全码。安全码取自 DrawImage.aspx 的 QueryString,并由 CodeImage 控件加密,以防止访问者在渲染的图片之外确定安全码。

如何使用

演示应用程序包含一个即插即用的示例,但对于现有的 ASP.NET Web Form,还需要做一些事情

  1. 将 AntiAuto 程序集复制到应用程序的 /bin 目录
    将 web.config 配置文件复制到应用程序的根目录
    将 DrawImage.aspx 复制到包含 Web Form 的同一目录
  2. 在 web form 顶部添加一个导入命令
    <%@ Register TagPrefix="etier" Namespace="Etier.AntiAuto" Assembly="AntiAuto" %>
    这会将命名空间映射到标签前缀,这样你就可以以 <prefix:ControlClass 的形式添加控件。
  3. 向 web form 添加一个 CodeImage 控件
    <etier:CodeImage
        ID="codeImageControl"
        RunAt="server"
    />
    在运行时,这会生成 <img src="..."> HTML 代码来生成图像。
  4. 向 web form 添加一个 CodeImageValidator 控件
    <etier:CodeImageValidator
        ID="codeImageValidator"
        ControlToValidate="codeTextBox"
        CodeImageControl="codeImageControl"
        ErrorMessage="Please enter the text in the image"
        Display="Static"
        RunAt="server"
    />
    重要的是 ControlToValidateCodeImageControl 必须设置为您为 TextBoxCodeImage 控件指定的 ID。

结论

这些控件非常简单,展示了 ASP.NET 新架构的辉煌。我希望人们觉得它有用并能改进它。抱歉代码中没有(或很少有)注释,但由于本文涵盖了它们如何协同工作,所以应该相对容易理解。当我重新审视解决方案时,我会把事情整理得更整洁一些,我肯定想做的一件事是制作一个单一的控件,以便部署更加容易。

如果您有任何问题或意见,请在下面留言或给我发电子邮件。

© . All rights reserved.