Add a honeypot-field to Devise registration form
-
09-06-2021 - |
Question
I will add a honeypot-field to the devise registration form (as a simple captcha). Where is the best place to check if data has been entered into this field?
If a bot has entered something into this field, he should be sent back to the index page without notification. As I am still far from being fluent in Rails, it would be helpful to see the syntax for this too.
Solution
I think the best place to do that is at the model, because it may be a business logic to accept only records from "verified humans".
You can create a validation method that does this:
class Comment < ActiveRecord::Base
validate :honeypot_absence
def honeypot_absence
record.errors.add :invisible_field, "You should not fill the invisible field" unless invisible.field.blank?
end
end
OTHER TIPS
I had a similar issue with bots signing up via my Devise registration form so tackled it by doing the following:
- Added a simple honeypot input to the view. (You will have needed to generate the views before you can do this -
rails generate devise:views
)
# app/views/devise/registrations/new.html.erb
<div class="form__honeypot">
If you see this, leave it blank. Only bots should see this
<input type="text" name="body" value="" />
</div>
- Hid the field using CSS.
.form {
position: relative;
&__honeypot {
position: absolute;
visibility: hidden;
left: -5000px;
top: -5000px;
}
}
- Generate the Devise controllers so you can tap into the
create
method.
rails g devise:controllers users -c=registrations
You can generate them all or just the one you need using the -c
flag.
- In the newly generated controller, added the following code:
# app/controllers/users/registrations_controller.rb
class Users::RegistrationsController < Devise::RegistrationsController
before_action :configure_sign_up_params, only: [:create]
def create
if params[:body].present?
return redirect_to root_path
end
super
end
def configure_sign_up_params
devise_parameter_sanitizer.permit(:sign_up, keys: [:body])
end
end
Essentially this is going to redirect anyone (hopefully bots), who populate the hidden field on the form, back to the root_url
- Finally in the
routes.rb
, added the line let Devise know to use our generated controller to handleregistrations
.
# config/routes.rb
Rails.application.routes.draw do
devise_for :users, controllers: { registrations: 'users/registrations' }
end