Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

ASP.NET Core Image Verification Code Implementation with Backend Validation

Tech Aug 26 46
// Verification method
public static string VerificationCodeCacheFormat = "vcode_cache_{0}";

public IActionResult GenerateVerificationCode()
{
    var verificationService = new VerificationCodeService();
    string code = "";
    using (var memoryStream = verificationService.CreateVerificationImage(out code))
    {
        code = code.ToLower(); // Case-insensitive verification code
        var token = Guid.NewGuid().ToString();
        var cacheKey = string.Format(VerificationCodeCacheFormat, token);
        _memoryCache.Set(cacheKey, code, new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromMinutes(20)));
        
        Response.Cookies.Append("verification_token", token);
        return File(memoryStream.ToArray(), "image/png");
    }
}

// Backend validation method
public bool ValidateUserInput(string userToken, string userInput)
{
    var cacheKey = string.Format(VerificationCodeCacheFormat, userToken);
    string storedCode = "";
    
    if (!_memoryCache.TryGetValue(cacheKey, out storedCode))
        return false;
    
    if (storedCode.ToLower() != userInput.ToLower())
        return false;
    
    _memoryCache.Remove(cacheKey);
    return true;
}

Generating verifiaction codes and images

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Threading.Tasks;

namespace Security.Services
{
    /// <summary>
    /// Image verification code generator
    /// </summary>
    public class VerificationCodeService
    {
        /// <summary>
        /// Generate a random string of specified length
        /// </summary>
        /// <param name="length">Length of the string</param>
        /// <returns>Random alphanumeric string</returns>
        private string GenerateRandomString(int length)
        {
            // Character set: 0-9, a-z, A-Z
            string chars = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,g,h,i,j,k,l,m,n,p,q,r,s,t,u,v,w,x,y,z,A,B,C,D,E,F,G,H,I,J,K,L,M,N,P,Q,R,S,T,U,V,W,X,Y,Z";
            
            string[] charArray = chars.Split(',');
            string code = "";
            int previousValue = -1;
            Random random = new Random();
            
            for (int i = 1; i <= length; i++)
            {
                if (previousValue != -1)
                {
                    random = new Random(i * previousValue * unchecked((int)DateTime.Now.Ticks));
                }
                
                int currentValue = random.Next(61);
                
                if (previousValue == currentValue)
                {
                    return GenerateRandomString(length); // Recursively generate if duplicate
                }
                
                previousValue = currentValue;
                code += charArray[currentValue];
            }
            
            return code;
        }

        /// <summary>
        /// Create verification image from string
        /// </summary>
        /// <param name="code">Verification code string</param>
        /// <param name="length">Number of characters (default 4)</param>
        /// <returns>Memory stream containing PNG image</returns>
        public MemoryStream CreateVerificationImage(out string code, int length = 4)
        {
            code = GenerateRandomString(length);
            Bitmap image = null;
            Graphics graphics = null;
            MemoryStream memoryStream = null;
            Random random = new Random();
            
            // Color palette
            Color[] colors = { Color.Black, Color.Red, Color.DarkBlue, Color.Green, Color.Orange, Color.Brown, Color.DarkCyan, Color.Purple };
            // Font collection
            string[] fonts = { "Verdana", "Microsoft Sans Serif", "Comic Sans MS", "Arial", "SimSun" };
            
            // Create image
            image = new Bitmap((int)code.Length * 18, 32);
            graphics = Graphics.FromImage(image);
            graphics.Clear(Color.White);
            
            // Draw background noise
            for (int i = 0; i < 100; i++)
            {
                int x = random.Next(image.Width);
                int y = random.Next(image.Height);
                graphics.DrawRectangle(new Pen(Color.LightGray, 0), x, y, 1, 1);
            }
            
            // Draw verification code
            for (int i = 0; i < code.Length; i++)
            {
                int colorIndex = random.Next(7);
                int fontIndex = random.Next(4);
                Font font = new Font(fonts[fontIndex], 15, FontStyle.Bold);
                Brush brush = new SolidBrush(colors[colorIndex]);
                int yPosition = 4;
                
                if ((i + 1) % 2 == 0)
                {
                    yPosition = 2;
                }
                
                graphics.DrawString(code.Substring(i, 1), font, brush, 3 + (i * 12), yPosition);
            }
            
            memoryStream = new MemoryStream();
            image.Save(memoryStream, ImageFormat.Png);
            graphics.Dispose();
            image.Dispose();
            
            return memoryStream;
        }
    }
}

The above implementation works for a single vreification code per page. Here's an improved version that supports multiple verfiication codes:

// Modified GenerateVerificationCode method to support multiple codes on a page
public IActionResult GenerateVerificationCode(string identifier = "")
{
    var verificationService = new VerificationCodeService();
    string code = "";
    using (var memoryStream = verificationService.CreateVerificationImage(out code))
    {
        code = code.ToLower();
        var token = Guid.NewGuid().ToString();
        var cacheKey = string.Format(VerificationCodeCacheFormat, token);
        _memoryCache.Set(cacheKey, code, new MemoryCacheEntryOptions()
            .SetSlidingExpiration(TimeSpan.FromMinutes(20)));
        
        Response.Cookies.Append($"verification_token_{identifier}", token);
        return File(memoryStream.ToArray(), "image/png");
    }
}

Single verification code on a page:

<p>
    Please enter the verification code: <br>
    <input type="text" id="verificationCode" class="form-control" />
    <img id="verificationImage" src="~/Home/GenerateVerificationCode" alt="Can't see clearly? Click to refresh" 
         onclick="this.src = this.src + '?'" style="vertical-align:middle;" />
</p>

Multiple verification codes on a page:

<p>
    <input name="verification_code" type="text" maxlength="5" id="otherCode" class="form-control tbText">
    <img id="otherVerificationImage" src="~/Home/GenerateVerificationCode?identifier=other" alt="Can't see clearly? Click to refresh" 
         onclick="this.src = 'GenerateVerificationCode?identifier=other&' + Math.random()" style="vertical-align:middle;" />
</p>

Tags: ASP.NET Core

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.