驗證碼是防止自動化腳本提交表單的基礎防線。ASP.NET 透過 `System.Drawing` (GDI+) 動態產生包含雜訊線、雜訊點、漸層文字的 GIF 圖片,並將驗證碼存入 Cookie 供後端驗證。本文提供完整前後端整合範例。
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Register.aspx.cs" Inherits="Demo.Register" %>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function changeImg() {
// 加隨機參數防止瀏覽器快取
$("#imgCheckNo").attr("src", "ValidateImage.aspx?r=" + Math.random());
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<img id="imgCheckNo" src="ValidateImage.aspx" style="border:1px solid #000; vertical-align:middle;" />
<span style="cursor:pointer; color:blue; margin-left:5px;" onclick="changeImg()">看不清?換一張</span>
</div>
<div>
<label>驗證碼:</label>
<asp:TextBox ID="tbxCheckNo" runat="server" CssClass="tbxCss" />
</div>
<div>
<asp:Button ID="btnSubmit" runat="server" Text="立即註冊" OnClick="btnSubmit_Click" />
</div>
</form>
</body>
</html>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="ValidateImage.aspx.cs" Inherits="Demo.ValidateImage" %>
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Web;
public partial class ValidateImage : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
// 無快取
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.Cache.SetNoStore();
string code = GenerateCode(4); // 4 碼
CreateImage(code);
}
private string GenerateCode(int length)
{
const string chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
var rand = new Random();
char[] code = new char[length];
for (int i = 0; i < length; i++)
code[i] = chars[rand.Next(chars.Length)];
string result = new string(code);
// 存入 Cookie (HttpOnly 防 XSS)
var cookie = new HttpCookie("ValidateCode", result) { HttpOnly = true };
Response.Cookies.Add(cookie);
return result;
}
private void CreateImage(string code)
{
int width = (int)Math.Ceiling(code.Length * 18.5);
int height = 28;
using (Bitmap bmp = new Bitmap(width, height))
using (Graphics g = Graphics.FromImage(bmp))
{
g.Clear(Color.White);
// 背景雜訊線
var rand = new Random();
for (int i = 0; i < 20; i++)
{
g.DrawLine(Pens.LightGray,
rand.Next(width), rand.Next(height),
rand.Next(width), rand.Next(height));
}
// 文字漸層
using (Font font = new Font("Consolas", 14, FontStyle.Bold | FontStyle.Italic))
using (var brush = new LinearGradientBrush(
new Rectangle(0, 0, width, height), Color.Blue, Color.DarkRed, 1.2f, true))
{
g.DrawString(code, font, brush, 2, 2);
}
// 前景雜訊點
for (int i = 0; i < 50; i++)
bmp.SetPixel(rand.Next(width), rand.Next(height), Color.FromArgb(rand.Next()));
// 邊框
g.DrawRectangle(Pens.Silver, 0, 0, width - 1, height - 1);
// 輸出 GIF
Response.ContentType = "image/gif";
using (MemoryStream ms = new MemoryStream())
{
bmp.Save(ms, ImageFormat.Gif);
Response.BinaryWrite(ms.ToArray());
}
}
}
}
protected void btnSubmit_Click(object sender, EventArgs e)
{
string cookieVal = Request.Cookies["ValidateCode"]?.Value;
if (string.IsNullOrEmpty(cookieVal) ||
!string.Equals(cookieVal, tbxCheckNo.Text, StringComparison.OrdinalIgnoreCase))
{
ClientScript.RegisterStartupScript(this.GetType(), "err", "alert('驗證碼錯誤!');", true);
return;
}
// 驗證通過,執行註冊邏輯...
ClientScript.RegisterStartupScript(this.GetType(), "ok", "alert('驗證碼正確!');", true);
}
| 項目 | 做法 | 效果 |
|---|---|---|
| 防快取 | Response.Cache.SetNoStore() + 圖片網址加隨機參數 | 確保每次請求重新產生 |
| Cookie 安全 | HttpOnly=true、Secure=true (HTTPS)、SameSite=Lax | 防 XSS、CSRF、跨站洩漏 |
| 大小寫不敏感 | StringComparison.OrdinalIgnoreCase | 提升使用者友善度 |
| 字型選擇 | Consolas / 等寬字型、斜體、粗體 | 增加機器辨識難度 |
| 雜訊干擾 | 背景線、前景點、漸層色、邊框 | 平衡人眼可讀與 OCR 難度 |
| 資源釋放 | using 區塊包裹 Bitmap、Graphics、Font、Brush、Stream | 防止 GDI+ 物件洩漏導致進程崩潰 |
本範例展示 ASP.NET 以 GDI+ 產生驗證碼圖片的完整流程:隨機字元產生 → Cookie 儲存 → 雜訊繪製 → 二進位輸出 → 後端比對。重點在於「資源正確釋放」、「Cookie 安全屬性」、「防快取」三大細節。現代專案建議優先評估第三方驗證服務,降低維護成本並提升安全性。