ASP.NET MVC - How to protect your website against spam

 
11/17/2009
.NET, ASP.NET, C#, MVC
9 Comments

When your use forms on your website, spammers will very soon be trying to inform your visitors about their "great" products. In this post I will show you how to protect your website by using a simple timestamp.

There are several possibilities to fight against spam:

  • Check the referrer to ensure the request comes from the correct origin. This only works in few cases, since spammers often supply cookies and the correct referrer.
  • You could use captchas. The disadvantage is, that many captchas are broken by spammers and your users have to decipher them, which is quite difficult sometimes.

But spammers have one problem: Since they want to spam as many websites as possible, they submit a form after only a few milliseconds. Humans however need at least a few seconds to fill a form before they submit it.

Implementation

To check if a request is performed by a human, we use a hidden field containing a timestamp. We use the following extension method of HtmlHelper to generate the timestamp:

public static class SpamProtectionExtensions
{
  public static string SpamProtectionTimeStamp(this HtmlHelper helper)
  {
    var builder = new TagBuilder("input");
    builder.MergeAttribute("id", "SpamProtectionTimeStamp");
    builder.MergeAttribute("name", "SpamProtectionTimeStamp");
    builder.MergeAttribute("type", "hidden");
    builder.MergeAttribute("value", ((long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds).ToString());
    return builder.ToString(TagRenderMode.SelfClosing);
  }
}

To validate the timestamp we use the following FilterAttribute:

public class SpamProtectionAttribute : FilterAttribute, IAuthorizationFilter
{
  public void OnAuthorization(AuthorizationContext filterContext)
  {
    long timestamp = long.MaxValue;
if (Int64.TryParse(filterContext.RequestContext.HttpContext.Request.Params["SpamProtectionTimeStamp"], out timestamp))
{
  long currentTime = (long)(DateTime.Now - new DateTime(1970, 1, 1)).TotalSeconds;

  if (currentTime <= timestamp + 1)
  {
    throw new HttpException("Invalid form submission. At least one seconds have to pass before form submission.");
  }
}
else
{
  throw new HttpException("Invalid form submission. Invalid timestamp parameter.");
}

} }

Usage

Usage is very similar to the AntiForgeryToken:

<% using (Html.BeginForm()) { %>
    <%= Html.TextBox("comment")%>
&lt;%= Html.SpamProtectionTimeStamp() %>
&lt;input type="submit" value="Submit" />

<% } %>

Now decorate your controller action handling the postback with the SpamProtectionAttribute:

  [SpamProtection()]
  [AcceptVerbs(HttpVerbs.Post)]
  public virtual ActionResult Entry(string comment)
  {
    // Your form handling
  }

Downloads

In my blog post 'ASP.NET MVC - Developing a custom blog engine' you'll find a project for download where the SpamProtectionAttribute is used to protect blog comments against spam.

Feedly Feedly Tweet


Related posts


Comments


Lars

Lars

11/4/2016

Thank you for this solution :) If you use .cshtml files (like me). Be sure let the SpamProtectionTimeStamp method return a HtmlString though. Like so: return new HtmlString(builder.ToString(TagRenderMode.SelfClosing)); You can then just use "@Html.AntiForgeryToken()" in your view (.cshtml)


Alejandro González

Alejandro González

9/7/2015

I thought so. Thanks for clarifying it, you are very kind!


Daniel

Daniel

9/7/2015

@Alejandro: Because most spam bots don't fill input fields that are completely hidden with display:none.


Alejandro González

Alejandro González

9/6/2015

Hi, Daniel. Very interesting article even though some time passed. I'm curious about why you hide the "website" input with absolute positioning. I assume spam bots don't pay attention to fill hidden inputs, but why not just make it invisible with display:none?


huobazi

huobazi

9/2/2015

retry retry retry retry ..... then currentTime > timestamp + 1


Daniel

Daniel

9/10/2013

@ben: Of course you could publish your code. I also extended my solution. I added a invisible field called "website". When the field is filled, I know the form was filled by a spammer. See: https://github.com/danielpalme/MVCBlog/tree/master/src/Palmmedia.Common/Net/Mvc


ben

ben

9/10/2013
http://www.xdev.net

Hi Daniel, i really like this extension and have extended it for my own use. Would you have any issue with my posting my changes to codeplex for further community development? I would of course credit you in the source.


Daniel

Daniel

4/27/2010

@Marc: Good idea. It's a neverending game between webmasters and spammers. I'm using the timestamp for quite a while now. In the beginning it worked really nice, but now some spammers are waiting a few seconds before they submit the form. To lock those spammers out I added a regular field to the form which is hidden with some CSS settings. The form is rejected if the field contains a value. After adding this field, I did not receive any spam at all.


Marc

Marc

4/27/2010

Good idea however there is nothing stopping the spammer from adjusting the timestamp value every time he makes a form submission so in which case you could encode the timestamp with a key and then decrypt it when checking the value.