Backend Development 14 min read

Implementation of Image and SMS Captcha Service in .NET Core

This article walks through the design and implementation of a complete captcha solution—including image and SMS verification—using .NET Core, covering background context, code details for generating graphics, unsafe handling, noise lines, caching strategies, rate limiting, validation logic, runtime results, and a link to the full source repository.

Top Architect
Top Architect
Top Architect
Implementation of Image and SMS Captcha Service in .NET Core

Background – The author describes moving to a new company in Xi'an, taking on the entire development lifecycle alone, and deciding to build a reusable captcha service (both image and SMS) after being inspired by a ThoughtWorks article on best practices.

Image Captcha Implementation – The service uses an ImageCaptchaHelper to generate random codes and render them as images. Key points include marking the drawing method as unsafe to use pointer‑based graphics, adjusting character positioning, and adding configurable noise lines to reduce OCR success. Sample code snippets illustrate the drawing routine and noise‑line generation:

void DrawCaptchaCode()
{
    SolidBrush fontBrush = new SolidBrush(Color.Black);
    int fontSize = GetFontSize(width, captchaCode.Length);
    Font font = new Font(FontFamily.GenericSerif, fontSize, FontStyle.Bold, GraphicsUnit.Pixel);
    for (int i = 0; i < captchaCode.Length; i++)
    {
        fontBrush.Color = GetRandomDeepColor();
        int shiftPx = fontSize / 6;
        float x = i * fontSize + rand.Next(-shiftPx, shiftPx) / 2;
        int maxY = height - fontSize * 2;
        if (maxY < 0) maxY = 0;
        float y = rand.Next(0, maxY);
        graph.DrawString(captchaCode[i].ToString(), font, fontBrush, x, y);
    }
}
void DrawDisorderLine()
{
    Pen linePen = new Pen(new SolidBrush(Color.Black), 2);
    for (int i = 0; i < 2; i++)
    {
        linePen.Color = GetRandomDeepColor();
        Point startPoint = new Point(rand.Next(0, width), rand.Next(0, height));
        Point endPoint = new Point(rand.Next(0, width), rand.Next(0, height));
        graph.DrawLine(linePen, startPoint, endPoint);
    }
}

The generated image and its code are cached using IMemoryCache with a composite key that includes the captcha type and mobile number.

SMS Captcha Implementation – A helper MsgCaptchaHelper creates a random numeric string of configurable length. The service validates the preceding image captcha, enforces a 60‑second request interval, stores the SMS code in cache for two minutes, and distinguishes between development and production environments (returning the code directly in dev). Sample logic for generation and validation is shown:

public static string CreateRandomNumber(int length)
{
    Random random = new Random();
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < length; i++)
    {
        sb.Append(random.Next(0, 9));
    }
    return sb.ToString();
}
public (bool, string) ValidateMsgCaptcha(MsgCaptchaDto dto)
{
    var key = $"MsgCaptcha{dto.MsgCaptchaType}{dto.Mobile}";
    var cached = _cache.Get
(key);
    if (cached == null) return (false, "Invalid code");
    if (cached.ValidateCount >= 3) { _cache.Remove(key); return (false, "Code expired"); }
    cached.ValidateCount++;
    if (!string.Equals(cached.MsgCaptcha, dto.MsgCaptcha, StringComparison.OrdinalIgnoreCase))
        return (false, "Wrong code");
    return (true, "Validated");
}

Rate limiting prevents more than one SMS request per minute, and a maximum of three validation attempts are allowed before the code is invalidated.

Running Effect – The article includes screenshots demonstrating the full workflow: obtaining an image captcha, successful/failed image verification, requesting an SMS code, handling frequency limits, and validating the SMS code with both correct and incorrect attempts.

Source Code – The complete solution is available at https://github.com/KINGGUOKUN/Captcha.git , packaged as a service‑oriented project ready for integration.

Summary – The implementation follows most of ThoughtWorks’ best‑practice checklist for captcha systems, with two noted gaps: it does not support multiple concurrent SMS codes per phone number and lacks integration with a real third‑party SMS provider. Both gaps can be addressed by storing a list of codes in cache and adding the external API call.

backendcachingSecuritycaptchaVerificationdotnet
Top Architect
Written by

Top Architect

Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.