Results 1 to 6 of 6
  1. #1
    faid's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Location
    Somewhere over the rainbow
    Posts
    547
    Reputation
    40
    Thanks
    63
    My Mood
    In Love

    Question ASP.NET Core Captcha

    Hi there I hope all of you are doing fine, I am wondering if there is anyone on this forum that knows their whereabouts in asp.net core MVC programming. I am having problems validating Captcha on my Login page.

    Basically, I am trying to make my Captcha validation work on my login page. The user will have to enter their userid, password, and captcha at the same time before logging in. My problem is that even if the captcha entered is incorrect or blank, my application will still enable the user to login with the correct userid and password. My guess is that the Account controller is not receiving any text input to validate the captcha.

    Please note that I am able to generate a captcha image but unable to validate the input text with the generated captcha.
    I also do not want to use any captcha API.

    I really do appreciate if anyone is able and willing to help me.

    Code snippet (Captcha Model):

    Code:
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Imaging;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.ComponentModel.DataAnnotations;
    
    
    namespace P12.Models
    {
        public class Captcha
        {
            const string Letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
            public static string GenerateCaptchaCode()
            {
                Random rand = new Random();
                int maxRand = Letters.Length - 1;
    
                StringBuilder sb = new StringBuilder();
    
                for (int i = 0; i < 4; i++)
                {
                    int index = rand.Next(maxRand);
                    sb.Append(Letters[index]);
                }
    
                return sb.ToString();
            }
    
            public static CaptchaResult GenerateCaptchaImage(int width, int height, string captchaCode)
            {
                using (Bitmap baseMap = new Bitmap(width, height))
                using (Graphics graph = Graphics.FromImage(baseMap))
                {
                    Random rand = new Random();
    
                    graph.Clear(GetRandomLightColor());
    
                    DrawCaptchaCode();
    
                    DrawDisorderLine();
    
                    AdjustRippleEffect();
    
                    MemoryStream ms = new MemoryStream();
    
                    baseMap.Save(ms, ImageFormat.Png);
    
                    return new CaptchaResult { CaptchaCode = captchaCode, CaptchaByteData = ms.ToArray(), Timestamp = DateTime.Now };
    
                    int GetFontSize(int imageWidth, int captchCodeCount)
                    {
                        var averageSize = imageWidth / captchCodeCount;
    
                        return Convert.ToInt32(averageSize);
                    }
    
                    Color GetRandomDeepColor()
                    {
                        int redlow = 160, greenLow = 100, blueLow = 160;
    
                        return Color.FromArgb(rand.Next(redlow), rand.Next(greenLow), rand.Next(blueLow));
                    }
    
                    Color GetRandomLightColor()
                    {
                        int low = 180, high = 255;
    
                        int nRend = rand.Next(high) % (high - low) + low;
                        int nGreen = rand.Next(high) % (high - low) + low;
                        int nBlue = rand.Next(high) % (high - low) + low;
    
                        return Color.FromArgb(nRend, nGreen, nBlue);
                    }
    
                    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) + rand.Next(-shiftPx, shiftPx);
                            int maxY = height - fontSize;
                            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), 3);
                        for (int i = 0; i < rand.Next(3, 5); 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);
    
                            Point bezierPoint1 = new Point(rand.Next(0, width), rand.Next(0, height));
                            Point bezierPoint2 = new Point(rand.Next(0, width), rand.Next(0, height));
    
                            graph.DrawBezier(linePen, startPoint, bezierPoint1, bezierPoint2, endPoint);
                        }
                    }
    
                    void AdjustRippleEffect()
                    {
                        short nWave = 6;
                        int nWidth = baseMap.Width;
                        int nHeight = baseMap.Height;
    
                        Point[,] pt = new Point[nWidth, nHeight];
    
                        double newX, newY;
                        double xo, yo;
    
                        for (int x = 0; x < nWidth; ++x)
                        {
                            for (int y = 0; y < nHeight; ++y)
                            {
                                xo = ((double)nWave * Math.Sin(2.0 * 3.1415 * (float)y / 128.0));
                                yo = ((double)nWave * Math.Cos(2.0 * 3.1415 * (float)x / 128.0));
    
                                newX = (x + xo);
                                newY = (y + yo);
    
                                if (newX > 0 && newX < nWidth)
                                {
                                    pt[x, y].X = (int)newX;
                                }
                                else
                                {
                                    pt[x, y].X = 0;
                                }
    
    
                                if (newY > 0 && newY < nHeight)
                                {
                                    pt[x, y].Y = (int)newY;
                                }
                                else
                                {
                                    pt[x, y].Y = 0;
                                }
                            }
                        }
    
                        Bitmap bSrc = (Bitmap)baseMap.Clone();
    
                        BitmapData bitmapData = baseMap.LockBits(new Rectangle(0, 0, baseMap.Width, baseMap.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                        BitmapData bmSrc = bSrc.LockBits(new Rectangle(0, 0, bSrc.Width, bSrc.Height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
    
                        int scanline = bitmapData.Stride;
    
                        IntPtr Scan0 = bitmapData.Scan0;
                        IntPtr SrcScan0 = bmSrc.Scan0;
    
                        unsafe
                        {
                            byte* p = (byte*)(void*)Scan0;
                            byte* pSrc = (byte*)(void*)SrcScan0;
    
                            int nOffset = bitmapData.Stride - baseMap.Width * 3;
    
                            int xOffset, yOffset;
    
                            for (int y = 0; y < nHeight; ++y)
                            {
                                for (int x = 0; x < nWidth; ++x)
                                {
                                    xOffset = pt[x, y].X;
                                    yOffset = pt[x, y].Y;
    
                                    if (yOffset >= 0 && yOffset < nHeight && xOffset >= 0 && xOffset < nWidth)
                                    {
                                        p[0] = pSrc[(yOffset * scanline) + (xOffset * 3)];
                                        p[1] = pSrc[(yOffset * scanline) + (xOffset * 3) + 1];
                                        p[2] = pSrc[(yOffset * scanline) + (xOffset * 3) + 2];
                                    }
    
                                    p += 3;
                                }
                                p += nOffset;
                            }
                        }
    
                        baseMap.UnlockBits(bitmapData);
                        bSrc.UnlockBits(bmSrc);
                        bSrc.Dispose();
                    }
                }
            }
        }
    
        public class CaptchaResult
        {
            [Required(ErrorMessage = "Captcha cannot be empty")]
            public string CaptchaCode { get; set; }
    
            public byte[] CaptchaByteData { get; set; }
    
            public string CaptchBase64Data
            {
                get
                {
                    return Convert.ToBase64String(CaptchaByteData);
                }
            }
    
            public DateTime Timestamp { get; set; }
        }
    }

    Code snippet (AccountController):

    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using P12.Models;
    using System.Security.Claims;
    using System.Data;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.AspNetCore.Http;
    
    namespace P12.Controllers
    {
        public class AccountController : Controller
        {
            private static string AuthScheme = "UserSecurity";
            private AppDbContext _dbContext;
    
            public AccountController(AppDbContext dbContext)
            {
                _dbContext = dbContext;
            }
    
            [HttpGet]
            public IActionResult Login(string CaptchaCode, string returnUrl = null)
            {
                ViewData["Layout"] = "_Layout";
                ViewData["ReturnUrl"] = returnUrl;
                return View();
            }
    
            [HttpPost]
            public IActionResult Login(string CaptchaCode, LoginUser user,
                                        string returnUrl = null)
            {
                ViewData["ReturnUrl"] = returnUrl;
                ClaimsPrincipal principal = null;
                bool isCaptchaValid = false;
    
                if (HttpContext.Session.GetString("CaptchaCode") != null && CaptchaCode == HttpContext.Session.GetString("CaptchaCode"))
                {
                    isCaptchaValid = true;
                }
    
                if (SecureValidUser(user.UserId, user.Password, out principal) && isCaptchaValid == true)
                {
                    HttpContext.Authentication.SignInAsync(AuthScheme, principal);
                    return RedirectToAction("Index", "Home");
                }
                
                else if (isCaptchaValid == false)
                {
                    ViewData["Message"] = "Captcha did not match! Please try again";
                    return View("Login");
    
                }
    
                else
                {
                    ViewData["Message"] = "Incorrect User ID or Password";
                    ViewData["Layout"] = "_Layout";
                    return View("Login");
                }
            }
    
            public IActionResult Logoff(string returnUrl = null)
            {
                HttpContext.Authentication.SignOutAsync(AuthScheme);
                return RedirectToAction("Index", "Home");
            }
    
            public IActionResult Forbidden(string returnUrl = null)
            {
                ViewData["Layout"] = "_Layout";
                return View();
            }
    
            private bool SecureValidUser(string uid,
                                         string pw,
                                         out ClaimsPrincipal principal)
            {
                string returnUrl = ViewData["ReturnUrl"] as string;
    
                string sql = "";
                sql = $"SELECT * FROM Staff WHERE Id='{uid}' AND Password = HASHBYTES('SHA1','{pw}')";
    
                DbSet<Staff> dbs = _dbContext.Staff;
                Staff staff = dbs.FromSql(sql)
                                      .FirstOrDefault();
    
    
                principal = null;
                if (staff != null)
                {
                    principal =
                       new ClaimsPrincipal(
                       new ClaimsIdentity(
                          new Claim[] {
                         new Claim(ClaimTypes.NameIdentifier,staff.Id.ToString()),
                         new Claim(ClaimTypes.Name, staff.Name),
                         new Claim(ClaimTypes****le, "staff")
                          },
                          "Basic"));
                    return true;
                }
                else
                {
                    return false;
                }
            }
    
        }
    }

    Code snippet (Login.cshtml)

    Code:
     @USING P12.Models
     @Model LoginUser
    @{
        Layout = ViewData["Layout"] as string;
        string msg = ViewData["Message"] as string;
    }
    
    <style>
        #table {
            height: auto;
            width: 700px;
        }
    </style>
    
    <div class="card card-info" id="table" style="background-color:#cfe2f3; margin-top:150px; margin-left:100px;">
        <div class="card-header" align="center">
            <h3 class="card-title">SIGN IN</h3>
        </div>
        <!-- /.card-header -->
        <!-- form start -->
        <form class="form-horizontal" method="post">
    
            @if (msg != null)
            {
                <div class="form-group">
                    <div class="col-sm-offset-1 col-sm-4">
                        <div class="alert alert-danger">
                            <strong>@msg</strong>
                        </div>
                    </div>
                </div>
            }
    
            <div class="card-body">
                <div class="form-group">
                    <label asp-for="UserId" class="col-sm-2 control-label">Email:</label>
    
                    <div class="col-sm-8">
                        <input asp-for="UserId" class="form-control" placeholder="Email address">
                        <div class="has-error">
                            <span asp-validation-for="UserId" class="text-danger"></span>
                        </div>
                    </div>
                </div>
                <div class="form-group">
                    <label asp-for="Password" class="col-sm-2 control-label">Password:</label>
                    <div class="col-sm-8">
                        <input asp-for="Password" class="form-control" placeholder="Password">
                        <div class="has-error">
                            <span asp-validation-for="Password" class="text-danger"></span>
                        </div>
                    </div>
                </div>
    
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-8">
                        <div class="form-check">
                            <input type="checkbox" class="form-check-input" id="remember">
                            <label class="form-check-label" title="Remember me" for="remember">Remember me</label>
                        </div>
                    </div>
                </div>
    
                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-8">
                        <a href="/reset/password" title="Reset your password" tabindex="3">Forgot password?</a>
                    </div>
                </div>
    
    
                <div id="divCaptcha">
                    @{
                        Html.RenderPartial("_Captcha");
                    }
                </div>
            </div>
            <!-- /.card-body -->
            <div class="form-group">
                <div class="col-sm-offset-2 col-sm-8">
                    <input type="submit" title="Sign In" class="btn btn-flat btn-primary btn-block" value="SIGN IN" />
                    @viewBag.IsVerified
                </div>
            </div>
    
            <div class="form-group">
                <div class="col-sm-offset-4 col-sm-8">
                    <div class="has-text-centered register">
                        <text>Not a member yet? </text><a href="/register" title="Register" tabindex="5">Register Now >></a>
                    </div>
                </div>
            </div>
            <!-- /.card-footer -->
        </form>
    </div>


    Code snippet (partial view _Captcha.cshtml):

    Code:
     @USING P12.Models
     @Model CaptchaResult
    
    
    
    <div class="form-group">
        <div class="col-sm-offset-2 col-sm-8">
            <h5><b><color style="color:red">Prove that you're not a robot.</color></b></h5>
            <img src="/Captcha/GetImage" />
            <input id="CaptchaCode" type="text" name="CaptchaCode" placeholder="Enter captcha here" />
        </div>
    </div>

  2. The Following User Says Thank You to faid For This Useful Post:

    Zaczero (06-03-2018)

  3. #2
    Hell_Demon's Avatar
    Join Date
    Mar 2008
    Gender
    male
    Location
    I love causing havoc
    Posts
    3,976
    Reputation
    343
    Thanks
    4,320
    My Mood
    Cheeky
    Your code is vulnerable to SQL injection.
    Ah we-a blaze the fyah, make it bun dem!

  4. The Following User Says Thank You to Hell_Demon For This Useful Post:

    faid (05-30-2018)

  5. #3
    faid's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Location
    Somewhere over the rainbow
    Posts
    547
    Reputation
    40
    Thanks
    63
    My Mood
    In Love
    Quote Originally Posted by Hell_Demon View Post
    Your code is vulnerable to SQL injection.
    Thank you for pointing that out. Would you mind suggesting me some workarounds or methods that I could follow to secure it.

    Quote Originally Posted by faid View Post
    Please note that I am able to generate a captcha image but unable to validate the input text with the generated captcha.
    I also do not want to use any captcha API.

    I really do appreciate if anyone is able and willing to help me.
    Finally, I managed to get everything working.

    Can someone help me close this thread thanks.
    @Smoke @Smoke's Sheep

  6. #4
    Hell_Demon's Avatar
    Join Date
    Mar 2008
    Gender
    male
    Location
    I love causing havoc
    Posts
    3,976
    Reputation
    343
    Thanks
    4,320
    My Mood
    Cheeky
    You could use Linq to Sql, we work with a custom ORM here so I'm not sure if there are any differences between this or stock linq to sql, but it should look something like this:

    Code:
    (from x in _dbContext.Staff
    where x.Id.Equals(Uid) && x.Password.Equals(HashSHA1(pw))
    select x).SingleOrDefault();
    Also note that I used SingleOrDefault, it does roughly the same as FirstOrDefault, but it throws an exception if there is more than 1 record in the resultset from the query. For a user table you should never have more than 1 result for a username, this enforces that nicely.

    Make sure the pw variable is already hashed with SHA1, in case you need a function for this:
    Code:
    static string HashSHA1(string input)
    {
        using (SHA1Managed sha1 = new SHA1Managed())
        {
            var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
            var sb = new StringBuilder(hash.Length * 2);
    
            foreach (byte b in hash)
            {
                sb.Append(b.ToString("X2"));
            }
    
            return sb.ToString();
        }
    }
    Ah we-a blaze the fyah, make it bun dem!

  7. The Following User Says Thank You to Hell_Demon For This Useful Post:

    faid (06-01-2018)

  8. #5
    faid's Avatar
    Join Date
    Sep 2011
    Gender
    male
    Location
    Somewhere over the rainbow
    Posts
    547
    Reputation
    40
    Thanks
    63
    My Mood
    In Love
    Quote Originally Posted by Hell_Demon View Post
    You could use Linq to Sql, we work with a custom ORM here so I'm not sure if there are any differences between this or stock linq to sql, but it should look something like this:

    Code:
    (from x in _dbContext.Staff
    where x.Id.Equals(Uid) && x.Password.Equals(HashSHA1(pw))
    select x).SingleOrDefault();
    Also note that I used SingleOrDefault, it does roughly the same as FirstOrDefault, but it throws an exception if there is more than 1 record in the resultset from the query. For a user table you should never have more than 1 result for a username, this enforces that nicely.

    Make sure the pw variable is already hashed with SHA1, in case you need a function for this:
    Code:
    static string HashSHA1(string input)
    {
        using (SHA1Managed sha1 = new SHA1Managed())
        {
            var hash = sha1.ComputeHash(Encoding.UTF8.GetBytes(input));
            var sb = new StringBuilder(hash.Length * 2);
    
            foreach (byte b in hash)
            {
                sb.Append(b.ToString("X2"));
            }
    
            return sb.ToString();
        }
    }
    Awesome, I'll try to implement this. Thank you.

  9. #6
    textverified's Avatar
    Join Date
    Jul 2018
    Gender
    male
    Location
    N/A
    Posts
    687
    Reputation
    16
    Thanks
    19
    Don't roll your own authentication mechanism. Stick with UserManager<ApplicationUser> and use that to resolve the ClaimsPrincipal. There's a lot that can go wrong with user authentication and M$ has done a good job encapsulating the cryptography. Just made your viewmodel take in the string and pair it against the expected captcha string. If it's a mismatched, add the error to the ModelState and call it a day.

  10. The Following User Says Thank You to textverified For This Useful Post:

    Hell_Demon (07-26-2018)

Similar Threads

  1. [Discussion] .Net Core 2 DLR?
    By ZunderCode in forum C# Programming
    Replies: 0
    Last Post: 12-05-2017, 10:58 AM
  2. Asp.net MVC C#
    By SteamThief in forum C# Programming
    Replies: 0
    Last Post: 12-07-2014, 09:16 AM
  3. ASP.NET or PHP?
    By t7ancients in forum PHP Programming
    Replies: 5
    Last Post: 02-10-2012, 08:12 AM
  4. [E-BOOKS]Complete e-books to learn VB.NET + VB + ASP.NET
    By ♪~ ᕕ(ᐛ)ᕗ in forum Visual Basic Programming
    Replies: 24
    Last Post: 09-11-2010, 03:42 AM
  5. ASP.NET/VB.NET[Official Discussion Thread]
    By NextGen1 in forum Visual Basic Programming
    Replies: 15
    Last Post: 04-09-2010, 03:52 AM

Tags for this Thread