验证码的作用
防止恶意破解密码、刷票、论坛灌水、刷页。验证码通常使用一些线条和一些不规则的字符组成,主要作用是为了防止一些黑客把密码数据化盗取。验证码还有一作用就是用于区分人类和计算机,达到阻止自动脚本反复提交垃圾数据的目的的技术。
验证码有以下几种:
最简单的图片验证码,网站方面随机生成一张图片,上面写着数字、字母或者汉字的组合,然后要求用户输入图片上的内容,并随表单一起提交。
攻破图片验证码的方法简介
主要手段是OCR技术,在20年前,OCR技术尚是一大难题,但是现在已经有了成熟的解决方案,OCR技术主要是基于神经网络人工智能的相 关研究成果实现。为了对付OCR技术,图片验证码有所升级,出现了杂点背景、扭曲文字等干扰机器识别的手段。对抗杂点背景的主要方法是通过颜色过滤杂点, 还有就是缩OCR目标范围,比如限制在26个英文字母而不对特殊符号加以识别等;对付扭曲文字干扰的方法主要是对文字纹路矢量化,然后计算他们的基线并还 原文字扭曲。以上两个方面都有不少相关的学术研究,搜索captcha hack可以得到一些具体资料。语音验证码常常作为图片验证码的补充,提供给有视觉障碍的人士使用。攻击的方法和图片验证码类似,音识别技术是对付它的法宝。当然不少语音验证码也使用了背景噪音等干扰,如何对付这种干扰又是另一个课题了。
智力测试验证码是 验证码的另一种设计思路,这种验证码比较有趣,也比较难攻击。它的工作原理是由服务器随机抽取一个简单的常识性智力题给最终用户,然后让最终用户作答。比 如在四张图片有三张风景,一张建筑物,然后让用户选出建筑物;再比如要求用户计算5+25等于多少,填写答案;还有的让用户回答电视机、电冰箱、电吹风、 电影票哪个不是家用电器。智力测试验证码方式繁多,五花八门,出题的方式可以文字亦可以图片。想攻破这种验证码具有相当难度,需要计算机具备高级 智慧还要兼用图像识别技术。不过至少针对文字测试题目可以利用自然语言分析技术和搜索引擎的帮助加以攻击。将验证码的提问用自然语言分词,找出主谓宾定状 补,并构造出关键词用Google搜索,再把结果计算权重投票,可以得到一个“最可能正确的答案”。另一种可能稍微简单些的方法就是收集攻击目标网站的题 库答案,然后兵来将挡,机器也能对答如流。
首先是验证码写在HttpHandler(一般应用程序)中的
代码如下:
using System; using System.Text; using System.Web; using System.Drawing; using System.Drawing.Imaging; using System.Web.SessionState; namespace Juice.Common.HttpHandlers { /// <summary> /// 验证码生成器 /// </summary> public sealed class ValidateCodeRender : IHttpHandler, IRequiresSessionState { /// <summary> /// 构造函数 /// </summary> public ValidateCodeRender() { } #region 私有字段 /// <summary> /// 验证码的长度 /// </summary> private int m_CodeLength = 5; /// <summary> /// 保存到会话状态中的值 /// </summary> private string m_SessionName = "validateCode"; /// <summary> /// 图像的宽 /// </summary> private int m_ImageWidth = 160; /// <summary> /// 图像的长 /// </summary> private int m_ImageHeight = 60; /// <summary> /// 图像背景色 /// </summary> private Color m_BackgroundColor = Color.FromArgb(212, 236, 189); /// <summary> /// 图像上验证码的颜色 /// </summary> private Color m_CodeColor = Color.FromArgb(132, 199, 1); /// <summary> /// 干扰图形的颜色 /// </summary> private Color m_ObstructionColor = Color.Gray; /// <summary> /// 用来生成随机验证码的字符串 /// </summary> private const string m_CodeString = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890我和你的大小"; #endregion #region IHttpHandler 成员 /// <summary> /// 是否可以重用 /// </summary> public bool IsReusable { get { return true; } } /// <summary> /// 处理请求 /// </summary> /// <param name="context"></param> public void ProcessRequest(HttpContext context) { ///设置输出的格式 context.Response.ContentType = "image/gif"; Random random = new Random(); ///用来保存随机取得的字符串 StringBuilder s = new StringBuilder(); ///创建绘图的图像 using (Bitmap bitmap = new Bitmap(this.m_ImageWidth, this.m_ImageHeight)) { ///创建绘图图面,用此对象将各种符号等绘制到制定的图像上 using (Graphics graphics = Graphics.FromImage(bitmap)) { ///生成背景颜色 graphics.FillRectangle(new SolidBrush(this.m_BackgroundColor), 0, 0, this.m_ImageWidth, this.m_ImageHeight); #region 生成验证码 ///验证码的字体 using (Font font = new Font(FontFamily.GenericSerif, 32, FontStyle.Bold | FontStyle.Italic, GraphicsUnit.Pixel)) { for (int i = 0; i < this.m_CodeLength; i++) { s.Append(ValidateCodeRender.m_CodeString.Substring(random.Next(0, m_CodeString.Length - 1), 1)); ///将字符绘制到图像上 graphics.DrawString(s[s.Length - 1].ToString(), font, new SolidBrush(this.m_CodeColor), i * 32, random.Next(0, 24)); } } #endregion #region 绘制干扰图形和噪点 //干扰图形绘制画笔 using (Pen pen = new Pen(new SolidBrush(this.m_ObstructionColor), 1)) { for (int i = 0; i < 10; i++) { graphics.DrawLine(pen, new Point(random.Next(0, this.m_ImageWidth - 1), random.Next(0, this.m_ImageHeight - 1)), new Point(random.Next(0, this.m_ImageWidth - 1), random.Next(0, this.m_ImageHeight - 1))); } } for (int i = 0; i < 100; i++) { //设置图片上的点 bitmap.SetPixel(random.Next(this.m_ImageWidth), random.Next(this.m_ImageHeight), Color.FromArgb(random.Next())); } #endregion ///保存图像到输出流 bitmap.Save(context.Response.OutputStream, ImageFormat.Gif); } } ///将验证码的值保存到用户会话状态中,并且不区分大小写 context.Session[this.m_SessionName] = s.ToString().ToLower(); context.Response.End(); } #endregion } }
ps:验证码字体大小固定,字体没有倾斜,没能对字体满眶显示进行处理,输出的是单个图片流,格式虽然是gif的,但不是动态的。HttpHandler容器中如果需要访问Session,必须实现IRequiresSessionState接口。
什么是一般处理程序?
请在在博客园中输入:HttpHandler或者一般处理程序进行搜索。或可阅读:摘自:http://www.cnblogs.com/lds85930/archive/2008/09/07/1286364.html 或摘自: http://www.cnblogs.com/hu88oo/articles/1348171.html
为什么要用一般处理程序呢?
有时我们响应给客户端的不是一个HTML页面,而是XML数据或者图片等,这时使用自定义的HttpHandler可能是不错的选择。
用一般处理程序处理好处:利用.ashx文件是一个更好的方法,这个文件类似于.aspx文件,可以通过它来调用HttpHandler类,从而免去了普通.aspx页面的控件解 析以及页面处理的过程。然后在同目录下,使用解决方案资源管理器,使用"添加"-->"添加类",在类文件名处输 入"TextBuilder.ashx.cs"。使用IE测试,输入这个.ashx的地址即可。
在应用程序中调用一般处理程序,又该怎么整呢?
<system.web> <httpHandlers> <add ver="*" path="" type=","/> </httpHandlers> </system.web>
verb:请求动作(get,post,*);
path:文件路径(*通配符);//请求的文件名
type:,前的参数为自定义HttpHandler或HttpHandlerFactory的完整类名(含命名空间),,后的参数为自定义HttpHandler或HttpHandlerFactory所在的程序集名(不含dll后缀)。//响应的程序
在应用程序中的验证码:
using System; using System.IO; using System.Data; using System.Configuration; using System.Collections; using System.Drawing; using System.Drawing.Imaging; using System.Drawing.Drawing2D; namespace HyeyClass.Common { /// <summary> /// ClassName:DrawValidateImg /// Auther:Hyey.wl /// Time:2010-04-30 /// Other: /// DrawValidateImg 的摘要说明。 /// </summary> public class DrawValidateImg { /// <summary> /// 无参构造 /// Initializes a new instance of the <see cref="DrawValidateImg"/> class. /// </summary> public DrawValidateImg() { // // TODO: 在此处添加构造函数逻辑 // } /// <summary> /// 带有生成字符个数的构造 /// Initializes a new instance of the <see cref="DrawValidateImg"/> class. /// </summary> /// <param name="charNum">The char num.验证码中包含随机字符的个数</param> public DrawValidateImg(int charNum) { this.CharNum = charNum; } /// <summary> /// 带有验证码图片宽度和高度的构造 /// Initializes a new instance of the <see cref="DrawValidateImg"/> class. /// </summary> /// <param name="width">The width.验证码图片宽度</param> /// <param name="height">The height.验证码图片高度</param> public DrawValidateImg(int width ,int height) { this.width = width; this.height = height; } /// <summary> /// 带有生成字符个数,验证码图片宽度和高度的构造 /// Initializes a new instance of the <see cref="DrawValidateImg"/> class. /// </summary> /// <param name="charNum">The char num.验证码中包含随机字符的个数</param> /// <param name="width">The width.验证码图片宽度</param> /// <param name="height">The height.验证码图片高度</param> public DrawValidateImg(int charNum ,int width ,int height) { this.CharNum = charNum; this.width = width; this.height = height; } /// <summary> /// 验证码中字符个数 /// </summary> int charNum = 5; //默认字符个数为5 /// <summary> /// 字数 /// Gets or sets the char num. /// </summary> /// <value>The char num.</value> public int CharNum { get { return charNum; } set { charNum = value; } } /// <summary> /// 字号 /// </summary> int fontSize =20; /// <summary> /// 字号 /// Gets the size of the font. /// </summary> /// <value>The size of the font.</value> public int FontSize { get { return fontSize; } } /// <summary> /// 图片宽度 /// </summary> int width=200; /// <summary> /// Gets the width. /// </summary> /// <value>The width.</value> public int Width { get { return width; } } /// <summary> /// 图片高度 /// </summary> int height=50; /// <summary> /// Gets or sets the height. /// </summary> /// <value>The height.</value> public int Height { get { return height; } set { height = value; } } /// <summary> /// 随机生成的字符串 /// </summary> string validStr=""; /// <summary> /// Gets or sets the valid STR. /// 随机生成的字符串 /// </summary> /// <value>The valid STR.</value> public string ValidStr { get { return validStr; } set { validStr = value; } } /// <summary> /// 产生指定个数的随机字符串,默认字符个数为5 /// </summary> void GetValidateCode() { Random rd = new Random(); //创建随机数对象 //产生由 charNum 个字母或数字组成的一个字符串 string str = "ABCDEFGHJKLMNPQRSTUVWYZ23456789";//共31个字符,除 l,o,x,I,O,X,1,0 的所有数字和大写字母 for (int i = 0; i < charNum; i++) { validStr = validStr + str[rd.Next(30)];//返回0到54 } } /// <summary> /// 由随机字符串,随即颜色背景,和随机线条产生的Image /// 返回 MemoryStream /// </summary> /// <returns>MemoryStream</returns> public MemoryStream GetImgWithValidateCode() { //产生随机字符串 GetValidateCode(); //声明一个位图对象 Bitmap bitMap = null; //声明一个绘图画面 Graphics gph = null; //创建内存流 using(MemoryStream memStream = new MemoryStream()) { Random random = new Random(); //由给定的需要生成字符串中字符个数 CharNum, 图片宽度 Width 和高度 Height 确定字号 FontSize, //确保不因字号过大而不能全部显示在图片上 int fontWidth=(int)Math.Round(width/(charNum+2)/1.3); int fontHeight=(int)Math.Round(height/1.5); //字号取二者中小者,以确保所有字符能够显示,并且字符的下半部分也能显示 fontSize = fontWidth <= fontHeight ? fontWidth : fontHeight; //创建位图对象 bitMap = new Bitmap(width+FontSize,height); //根据上面创建的位图对象创建绘图图面 gph = Graphics.FromImage(bitMap); //设定验证码图片背景色 gph.Clear(GetControllableColor(200)); //gph.Clear(Color.White); //产生随机干扰线条 // for (int i = 0; i < 10; i++) // { // Pen backPen = new Pen(GetControllableColor(100), 2); // //线条起点 // int x = random.Next(width); // int y = random.Next(height); // //线条终点 // int x2 = random.Next(width); // int y2 = random.Next(height); // //划线 // gph.DrawLine(backPen, x, y, x2, y2); // } //定义一个含10种字体的数组 String[] fontFamily ={ "Arial", "Verdana", "Comic Sans MS", "Impact", "Haettenschweiler", "Lucida Sans Unicode", "Garamond", "Courier New", "Book Antiqua", "Arial Narrow" }; // SolidBrush sb = new SolidBrush(GetControllableColor(0)); PointF Cpoint1=new PointF(5,5); Random rnd1 = new Random(); int x1 = 0, y1 = 0; //通过循环,绘制每个字符, for (int i = 0; i < validStr.Length; i++) { //随机字符位置 x1 = rnd1.Next(10)+40*i; y1 = rnd1.Next(bitMap.Height/4); Cpoint1 = new PointF(x1,y1); //随机字符颜色,应根据背景作适当调整以免显示模糊不清 SolidBrush sb = new SolidBrush(Color.FromArgb(rnd1.Next(100), rnd1.Next(100), rnd1.Next(100))); //Font textFont = new Font(fontFamily[random.Next(10)], fontSize, FontStyle.Bold);//字体随机,字号大小30,加粗 Font textFont = new Font("Arial",35 , FontStyle.Bold);//字体随机,字号大小30,加粗 //随机倾斜字符 gph.TranslateTransform(10, 0); Matrix transform = gph.Transform; transform.Shear(Convert.ToSingle( rnd1.NextDouble()-0.5),0.001f); gph.Transform = transform; //每次循环绘制一个字符,设置字体格式,画笔颜色,字符相对画布的X坐标,字符相对画布的Y坐标 //int space = (int)Math.Round((double)((width - fontSize * (CharNum + 2)) / CharNum)); //纵坐标 int y = (int)Math.Round((double)((height - fontSize)/3)); // gph.DrawString(validStr.Substring(i, 1), textFont, sb, fontSize + i * (fontSize + space), y); gph.DrawString(validStr.Substring(i, 1), textFont, sb, Cpoint1); gph.ResetTransform(); } //扭曲图片 //bitMap=TwistImage(bitMap, true, random.Next(3,5),random.Next(3)); //画图片的前景噪音点 // for (int i = 0; i < 10; i++) // { // int x = random.Next(bitMap.Width); // int y = random.Next(bitMap.Height); // bitMap.SetPixel(x, y, Color.White); // } //画图片的边框线 gph.DrawRectangle(new Pen(Color.Black,2), 0, 0, bitMap.Width - 1, bitMap.Height - 1); try { bitMap.Save(memStream, ImageFormat.Gif); } catch (Exception ex) { System.Windows.Forms.MessageBox.Show(ex.Message); } bitMap.Dispose(); // Image img = Image.FromStream(memStream); // gph.DrawImage(img, 50, 20, width, 10); //gph.Dispose(); //return img; return memStream; } } /// <summary> /// 产生一种 R,G,B 均大于 colorBase 随机颜色,以确保颜色不会过深 /// </summary> /// <param name="colorBase">The color base.</param> /// <returns>背景色</returns> Color GetControllableColor(int colorBase) { Color color=Color.Black; if (colorBase > 200) { System.Windows.Forms.MessageBox.Show("可控制颜色参数大于200,颜色默认位黑色"); } Random random = new Random(); //确保 R,G,B 均大于 colorBase,这样才能保证背景色较浅 color= Color.FromArgb(random.Next(56) + colorBase, random.Next(56) + colorBase, random.Next(56) + colorBase); return color; } /// <summary> /// 扭曲图片 /// </summary> /// <param name="srcBmp">The SRC BMP.</param> /// <param name="bXDir">if set to <c>true</c> [b X dir].</param> /// <param name="dMultValue">The d mult value.</param> /// <param name="dPhase">The d phase.</param> /// <returns></returns> Bitmap TwistImage(Bitmap srcBmp, bool bXDir, double dMultValue, double dPhase) { int leftMargin = 0; int rightMargin = 0; int topMargin = 0; int bottomMargin = 0; //float PI = 3.14159265358979f; float PI2 = 6.28318530717959f; Bitmap destBmp = new Bitmap(srcBmp.Width, srcBmp.Height); double dBaseAxisLen = bXDir ? Convert.ToDouble(destBmp.Height) : Convert.ToDouble(destBmp.Width); for (int i = 0; i < destBmp.Width; i++) { for (int j = 0; j < destBmp.Height; j++) { double dx = 0; dx = bXDir ? PI2 * Convert.ToDouble(j) / dBaseAxisLen : PI2 * Convert.ToDouble(i) / dBaseAxisLen; dx += dPhase; double dy = Math.Sin(dx); //取得当前点的颜色 int nOldX = 0; int nOldY = 0; nOldX = bXDir ? i + Convert.ToInt32(dy * dMultValue) : i; nOldY = bXDir ? j : j + Convert.ToInt32(dy * dMultValue); System.Drawing.Color color = srcBmp.GetPixel(i, j); if (nOldX >= leftMargin && nOldX < destBmp.Width - rightMargin && nOldY >= bottomMargin && nOldY < destBmp.Height - topMargin) { destBmp.SetPixel(nOldX, nOldY, color); } } } return destBmp; } /// <summary> /// 判断验证码是否正确 /// </summary> /// <param name="inputValCode">待判断的验证码</param> /// <returns>正确返回 true,错误返回 false</returns> public bool IsRight(string inputValCode) { if (validStr.ToUpper().Equals(inputValCode.ToUpper()))//无论输入大小写都转换为大些判断 { return true; } else { return false; } } } }
找到一个动态动态验证码,摘自:http://www.cnblogs.com/kingboy2008/p/3506416.html 代码类似于12306。 但是代码没能明白。他的博客中附有源码。
找到一个中文验证码,地址:http://www.cnblogs.com/SpeakHero/p/3203690.html
验证码的其他思路:http://www.cnblogs.com/liubaolongcool/archive/2011/04/29/2032392.html 这里面有不同思路的验证码。