bot-away

DESCRIPTION:

Unobtrusively detects form submissions made by spambots, and silently drops those submissions. The key word here is “unobtrusive” – this is NOT a CAPTCHA. This is a transparent, modular implementation of the bot-catching techniques discussed by Ned Batchelder at nedbatchelder.com/text/stopbots.html.

WHAT’S GOING ON:

If a bot submission is detected, the params hash is cleared, so the data can’t be used. Since this includes the authenticity token, Rails should complain about an invalid or missing authenticity token. Congrats, spam blocked.

The specifics of the techniques employed for filtering spambots are discussed Ned’s site in the description; however, here’s a brief run-down of what’s going on:

  • Your code stays the same. After the bot-away gem has been activated, all Rails-generated forms on your site will automatically be transformed into bot-resistent forms.

  • All of the form elements that you create (for instance, a “comment” model with a “body” field) are turned into dummy elements, or honeypots, and are made invisible to the end user. This is done using div elements and inline CSS stylesheets. There are several ways an element can be hidden, and these approaches are chosen at random to help minimize predictability. In the rare event that a real user actually can see the element, it has a label next to it along the lines of “Leave this blank” – though the exact message is randomized to help prevent detection.

  • All of the form elements are mirrored by hashes. The hashes are generated using the session’s authenticity token, so they can’t be predicted.

  • When data is submitted, bot-away steps in and 1.) validates that no honeypots have been filled in; and 2) converts the hashed elements back into the field names that you are expecting (replacing the honeypot fields).

  • If a honeypot has been filled in, or a hashed element is missing where it was expected, then the request is considered to be either spam, or tampered with; and the entire params hash is emptied. Since this happens at the lowest level, the most likely result is that Rails will complain that the user’s authenticity token is invalid. If that does not happen, then your code will be passed a params hash containing only a “suspected_bot” key, and an error will result. Either way, the spambot has been foiled!

FEATURES/PROBLEMS:

  • Wherever protection from forgery is not enabled in your Rails app, the Rails forms will be generated as if this gem did not exist. That means hashed elements won’t be generated, honeypots won’t be generated, and posted forms will not be intercepted.

  • By default, protection from forgery is enabled for all Rails controllers, so by default the above-mentioned checks will also be triggered. For more details on forgery protection, see: api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html

  • The techniques implemented by this library will be very difficult for a spambot to circumvent. However, keep in mind that since the pages have to be machine-readable by definition, and since this gem has to follow certain protocols in order to avoid confusing lots of humans (such as hiding the honeypots), it is always theoretically possible for a spambot to get around it. It’s just very, very difficult.

  • I feel this library has been fairly well-tested (99.5% test coverage as of this writing), but if you discover a bug and can’t be bothered to let me know about it (or you just don’t have time to wait for a fix or fix it yourself), then you can simply add the name of the offending form element to the ActionController::Request.unfiltered_params array like so:

    ActionController::Request.accepts_unfiltered_params 'role_ids'
    ActionController::Request.accepts_unfiltered_params 'user' # this can be called multiple times
    

    You should also take note that this is an array, not a hash. So if you have a user as well as a group, the role_ids will not be matched on EITHER of these models.

  • Currently, there’s no direct support for per-request configuration of unfiltered params. This is mostly due to Bot-Away’s low-level approach to filtering bots: the params have already been filtered by the time your controller is created. I’d like to revisit per-request filtering sometime in the future, once I figure out the best way to do it.

REQUIREMENTS:

  • Rails 2.3.5 or better.

INSTALL:

  • sudo gem install bot-away

USAGE:

In your Rails config/environment.rb:

  • config.gem ‘bot-away’

That’s it.

LICENSE:

(The MIT License)

Copyright © 2010 Colin MacKenzie IV

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.