Honeypot implementation in MVC

Honeypot bot detection implementation in MVC

  • Share

You can find a bunch of articles on internet about HONEYPOT implementation on web pages, but in short it is a replacement for CAPTCHA anti-bot security.

In CAPTCHA scenario, user needs to enter hardly recognizable characters in order to prove that he is not a bot trying to submit data. Sometime that is really annoying but it is a way to filter automated posts created from bots.

HONEYPOT is another approach which does not require any action from human page visitor. It is basically trying to trick bot to write the value in the wrong field and submit the data while real data from manual input is captured from some non meaningful named field.

Basic Honeypot

This can be easily done by creating these two fields manually, but over time bots can figure out and use that dummy field.

<input name="6D9A89AAA95B1B3BFD6C7C5A6D5535FF" type="text" id="6D9A89AAA95B1B3BFD6C7C5A6D5535FF" />
<input name="Email" type="hidden" id="Email" />
    

On this scenario, implementation I wrote for MVC4 is based on. It generates two fields but real value field without meaningful name is always different (for each session). This makes harder for bot to figure out which field is the actual value and which one is trap.

@Html.HoneyPotField("Email", Model.Email, null, HtmlHelpers.InputType.Text, "masked", HtmlHelpers.InputType.Email)
    

This also produces a problem for binding form fields collection to action model in a controller. To make this work and make it easy and reusable I decided to change forms field collection before binding model in controller action.

This is done in action filter in OnActionExecuting. Of course it would be to easy if it is just like this :)

The problem is that model binding is already done when OnActionExecuting is invoked. We will need to use reflection to inject data from form collection to a model of action.

        private void SetIsTrapped(bool value)
        {
            this.isTrapped = value;
            if (value)
            {
                var collection = HttpContext.Current.Request.Form;
                var propInfo = collection.GetType().GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
                propInfo.SetValue(collection, false, new object[] { });
                collection.Add("HasHoneypotTrapped", true.ToString());
            }
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            SetIsTrapped(false);

            var collection = HttpContext.Current.Request.Form;
            foreach (string field in honeypots)
            {
                if (!string.IsNullOrWhiteSpace(collection[field]))
                {
                    SetIsTrapped(true);
                }
                {
                    string hashedName = Mvc.Honeypot.HtmlHelpers.GetHashedPropertyName(field);
                    if (collection.AllKeys.Contains(hashedName))
                    {
                        string val = HttpContext.Current.Request.Form[hashedName];
                        foreach (var actionValue in filterContext.ActionParameters)
                        {
                            foreach (var prop in actionValue.Value.GetType().GetProperties())
                            {
                                if (prop.Name == field && prop.CanWrite && prop.PropertyType == typeof(string))
                                {
                                    if (prop.PropertyType == val.GetType())
                                    {
                                        prop.SetValue(actionValue.Value, val);
                                    }
                                }
                            }
                        }
                    }
                }
            }

        }
    

Method SetIsTrapped adds value to form collection which is used to identify whether request came from bot or not.

To make it easier, we can use extension method on a Request type

        public static bool HasHoneypotTrapped(this HttpRequestBase request)
        {
            if (request.Form.AllKeys.Contains("HasHoneypotTrapped") &&
                !string.IsNullOrWhiteSpace(request.Form["HasHoneypotTrapped"]) && request.Form["HasHoneypotTrapped"].Equals(true.ToString(), StringComparison.InvariantCultureIgnoreCase))
            {
                return true;
            }
            return false;
        }
    

Now we can easily check whether request is coming from bot or not by checking the result of HasHoneypotTrapped.

You can download complete code from attachment of this article or fork it from GitHub or just include binaries from NuGet.

References

  • Share

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.

Comments for this article

comments powered by Disqus