My little world of sharing

Apr06

Prevent your MVC application from brute force attack

Programmers and developers develop every kind of applications for various purposes. In most cases, the applications are secure and less vulnerable when a skilled and experienced developer develops them. Testing for possible security issues and vulnerability of your app is paramount. This should not be neglected, imagine you have developed a system for a financial institution or a corporate company which holds a lot of sensitive information, you realised soon enough the system has been compromised or exploited. What could be the consequences?

At least you should consider the following security measures and testings if you want to build a secure system for your client:

  •  SQL injection testing – hackers try to find the back door through various exploitation mechanism. The SQL injection is one of the common approaches they do to find the back-door and get access to the database. If someone can access your data then can compromise the whole system. Anyway, this is out of scope to describe this testing and measures in this topic. You can read some SQL injection techniques at https://www.w3schools.com/sql/sql_injection.asp
  • Brute Force attack – this is another common attack used by hackers to retrieve the username and password through the scripts. The application login screen can be used to exploit the system and retrieve the login details. This kind of attack could be dangerous. Imagine you are running an online store and taking online payments and stores credit card details through your website. Now if someone can gain access to your admin account or a customer account. They can do all sorts of things. I am going to discuss this here.
  • Protecting your code for unauthorize access – Make sure you don’t display your code when the application crashes or on runtime error. You should handle the error in professional way e.g redirect the user to an error page, log the error in the server or system, notify admin etc. Never ever display broken code to the browser because this can lead to potential exposure of sensitive information like database script, PHP, C# code etc.
  • Strong password policy and account lockout policy – this should be practised strongly in any corporate system. The policy should include account lockout after a number of unsuccessful logins. Some developers use timing lockout which means the account will be locked for certain time e.g. 30 mins, 1 hour etc. I personally like the timing lockout, it reset the account without admin interaction of manually unlocking the account. Again it depends on the nature of the system.

brute-force

How can we prevent brute force attack?

The new MVC 4 application provides an anti-forgery token for every form on the page, however, this is not a mandatory feature which means you might have to add it manually if you are not using inbuilt scaffolding process. Basically, the system creates a randomly token on page rendering and validate the token on form post. This is very handy to prevent a system from XSS (Cross Site Scripting) attack. Here is an example of an anti-forgery token.

<form action="/Account/Login" class="form-login" method="post" role="form"><input name="__RequestVerificationToken" type="hidden" value="KBTbuLxZbfMQovBbNXjEfEdlOdiKIEWJrAHoWiUDLqEORVgP9IR4Rys1FegV8vAXjskpzuZPAsQhsznkNaaUZdXWTbgKV55P1tz9NAtWI4g1" />
<div class="form-group">
                            <label class="col-md-2 control-label" for="UserName">Username</label>
                           
                                <input class="form-control input-sm bounceIn animation-delay2" data-val="true" data-val-required="The Username field is required." id="UserName" name="UserName" placeholder="Username" type="text" value="zoombie" />
                                <span class="field-validation-valid text-danger" data-valmsg-for="UserName" data-valmsg-replace="true"></span>
                            
                        </div>
                        
                        <hr />
                        <input type="submit" class="btn btn-success btn-sm bounceIn animation-delay5 btn-block" value="Login" />
</form>                </div>

In the above code, the page inject a hidden field called “_RequestVerificationToken” which the value of long code e.g. KBTbuLxZbfMQovBbNXjEfEdlOdiKIEWJrAHoWiUDLqEORVgP9IR4Rys1FegV8vAXjskpzuZPAsQhsznkNaaUZdXWTbgKV55P1tz9NAtWI4g1

This gets crossed checked when a user posts the form through the browser. Everytime user posts the form, it generates a new code which makes attacker so hard to perform the cross-domain attack.

Remember, you must need to add the anti-forgery token [ValidateAntiForgeryToken] in the controller action to implement this. Here is an example.

 // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
          //your code implementation here
       }

Now, this is an inbuilt security measure out of the box from ASP.NET MVC application. You can even take it one step further to implement captcha to prevent unwanted login attempt to the system. Most of you are familiar with Google Recaptcha, in fact, this is one of the most popular frameworks now developers user to prevent their apps from brute-force attack. I guess another reason for this to become more popular is because it has the libraries for all most all languages. It becomes so easy to integrate with any system. There are always pros and cons when it comes to using a third party library. Most of the third party open source libraries have some kind of interest to collect data from the website where their library has been used. It does not mean that they are bad, it just worries me some kind of the nature of the application. Sometimes third-party plugins or libraries can increase the vulnerability of the system due to that plugin or library security issue.

Anyway, this topic is for those who does not want to use any third party library for this. The solution for them is to create your own captcha. It is not very hard, in fact quite easy to create your own simple captcha to give extra security to your application. The idea is to implement captcha in the login screen which will look like this.

secure-login-screen

 

From the above screen, you can see the user is required to enter the simple security code along with the username and password to gain access to the system. Attackers cannot attempt to perform brute force attack since the page will reject the request if the request does not contain the valid security code. The captcha code is generated in an image format because so that script cannot read the code. This is also a very important point, if this is not in image format then you can write your own script to read the value from the page and perform the form post action. The application creates a random string in this case number and saves this in the session value which can be cross checked on form post. Here is the code for the cshtml page.

@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-login", role = "form" }))
{
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                   
                        <div class="form-group">
                            @Html.LabelFor(m => m.UserName, new { @class = "col-md-2 control-label" })
                           
                                @Html.TextBoxFor(m => m.UserName, new { @class = "form-control input-sm bounceIn animation-delay2", placeholder="Username" })
                                @Html.ValidationMessageFor(m => m.UserName, "", new { @class = "text-danger" })
                            
                        </div>
                        <div class="form-group">
                            @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
                                @Html.PasswordFor(m => m.Password, new { @class = "form-control input-sm bounceIn animation-delay4", @placeholder="Password" })
                                @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
                               
                            </div>
                            <div class="form-group form-inline">

                                <div class="input-group col-md-6">
                                    <span class="input-group-addon">
                                        <i class="fa fa-key"></i>
                                    </span>
                                    @Html.TextBoxFor(model => model.SecurityCode, new { @class = "form-control input-sm bounceIn animation-delay4", @placeholder="Enter the code" })

                                </div>
                                <div class="col-md-5 pull-right">
                                    <img alt="" src="~/Account/captcha" />
                                </div>
                                @Html.ValidationMessageFor(model => model.SecurityCode, "", new { @class = "text-danger" })
                            </div>

                        <div class="seperator"></div>
                     
                        <hr />

                        <input type="submit" class="btn btn-success btn-sm bounceIn animation-delay5 btn-block" value="Login" />
                    
}
Optional Title
Please note, I have excluded irrelevant codes from the above code block.

In the above code block <img alt=”” src=”~/Account/captcha” /> loads the image by calling the controller action. The controller action for the captcha code –

 //create captcha
        [AllowAnonymous]
      public ActionResult Captcha()
        {
            Bitmap objBMP = new System.Drawing.Bitmap(60, 30);
            Graphics objGraphics = System.Drawing.Graphics.FromImage(objBMP);
            objGraphics.Clear(Color.DimGray);
            objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
            //' Configure font to use for text
            Font objFont = new Font("Calibri", 14, FontStyle.Bold);
            string randomStr = "";
            int[] myIntArray = new int[5];
            int x;
            //That is to create the random # and add it to our string
            Random autoRand = new Random();
            for (x = 0; x < 5; x++)
            {
                myIntArray[x] = System.Convert.ToInt32(autoRand.Next(0, 9));
                randomStr += (myIntArray[x].ToString());
            }
            //This is to add the string to session cookie, to be compared later
            Session.Add("randomStr", randomStr);
            //' Write out the text
            objGraphics.DrawString(randomStr, objFont, Brushes.White, 4, 4);
            //' Set the content type and return the image
            Response.ContentType = "image/GIF";
            objBMP.Save(Response.OutputStream, ImageFormat.Gif);

            objFont.Dispose();
            objGraphics.Dispose();
            objBMP.Dispose();

            return new EmptyResult();
        }

The above code creates a random string and adds the string to the session value. Which will be ready on page load when the action is called. This can be now verified with user input. No zoomibies allowed in this secure system.

secure-login-error

 

Once you get the captcha ready, you have to implement the validation code in the login action event in the AccountController. Here is how I check the user input.

 

 // POST: /Account/Login
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
        {
            if (!ModelState.IsValid)
            {
                return  View(model);
            }
            

            if (model.SecurityCode == Session["randomStr"].ToString())
            {

               //login implementation code goes here
              //I have excluded this code block since it is not relevant to this tutorial
               
            }
            else
            {
                ModelState.AddModelError("", "Error: Please enter the security code correctly");
                return this.View(model);
            }
            //log user activity
            LogError.LogUserActivity(model.UserName, "Login failed", "Login");
            this.ModelState.AddModelError(string.Empty, "The user name or password provided is incorrect.");

            return this.View(model);
           
        }

I am using a  login Model view for the login form. I have included the extra field there as a required field. This will reduce a lot of manual coding works and make it cleaner approach. Make sure this field is set to NotMapped since you don’t want to create a column for this field in the database. My login view model like this –

public class LoginViewModel
    {
        [Required]
        [Display(Name = "Username")]
        public string UserName { get; set; }

        [Required]
        [DataType(DataType.Password)]
        [Display(Name = "Password")]
        public string Password { get; set; }

        [Display(Name = "Remember me?")]
        public bool RememberMe { get; set; }

        [Display(Name = "Security Code"), MaxLength(5, ErrorMessage = "Must be less than 6 digits."), NotMapped, Required(ErrorMessage = "Enter the security code")]
      //  [Range(0, int.MaxValue, ErrorMessage = "Please enter number only")]
        public string SecurityCode { get; set; }
    }

 

 

Happy programming.

 

Leave a Comment

Your email address will not be published. Required fields are marked *