From a3ef16bc8c6e8b8878fd9d5315c19c1d32babf3b Mon Sep 17 00:00:00 2001 From: mgabdev <> Date: Tue, 26 Jan 2021 15:04:05 -0500 Subject: [PATCH] Added captcha for sign up form MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • Added: - captcha for sign up form - empty ENV variables for: GAB_CAPTCHA_SECRET_KEY, GAB_CAPTCHA_CLIENT_KEY to be configured within captcha.gab.com - Script insertion within registrations/new.html.haml containing instantiation for captcha.gab.com with our client key and challenge buster - Div within registration form for #gab-captcha for the challenge to get inserted within - Checks in RegistrationsController for captcha verification using server token (automatically generated in form), secret [server] key before checking if username/password/email is valid --- .env.nanobox | 4 ++ .env.production.sample | 4 ++ .../auth/registrations_controller.rb | 59 +++++++++++++++---- app/views/auth/registrations/new.html.haml | 17 ++++-- 4 files changed, 68 insertions(+), 16 deletions(-) diff --git a/.env.nanobox b/.env.nanobox index da01a1d3..73d266db 100644 --- a/.env.nanobox +++ b/.env.nanobox @@ -224,3 +224,7 @@ SMTP_FROM_ADDRESS=notifications@${APP_NAME}.nanoapp.io # SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1" # SAML_ATTRIBUTES_STATEMENTS_VERIFIED= # SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL= + +# Gab Captcha +GAB_CAPTCHA_SECRET_KEY= +GAB_CAPTCHA_CLIENT_KEY= \ No newline at end of file diff --git a/.env.production.sample b/.env.production.sample index 49f9426e..466c1936 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -231,3 +231,7 @@ STREAMING_CLUSTER_NUM=1 # http_proxy=http://gateway.local:8118 # Access control for hidden service. # ALLOW_ACCESS_TO_HIDDEN_SERVICE=true + +# Gab Captcha +GAB_CAPTCHA_SECRET_KEY= +GAB_CAPTCHA_CLIENT_KEY= \ No newline at end of file diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index 1c51b4f0..61dda72e 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -3,26 +3,22 @@ class Auth::RegistrationsController < Devise::RegistrationsController layout :determine_layout - before_action :set_challenge, only: [:new] before_action :check_enabled_registrations, only: [:new, :create] before_action :configure_sign_up_params, only: [:create] before_action :set_sessions, only: [:edit, :update] before_action :set_instance_presenter, only: [:new, :create, :update] before_action :set_body_classes, only: [:new, :create, :edit, :update] before_action :set_cache_headers, only: [:edit, :update] + prepend_before_action :check_captcha, only: [:create] def new + set_challenge_buster super end def create - if session[:challenge_answer].to_s == params[:user][:challenge].to_s.strip - # Reset after, may be errors to return and this ensures its still visible - set_challenge - super - else - return false - end + set_challenge_buster + super end def destroy @@ -66,6 +62,18 @@ class Auth::RegistrationsController < Devise::RegistrationsController private + def check_captcha + unless passed_challenge?(params["gab-captcha-st"], params[:user]) + self.resource = resource_class.new configure_sign_up_params + resource.validate # Look for any other validation errors besides reCAPTCHA + flash[:captcha_error] = "Incorrect text. Please try again." + set_challenge_buster + respond_with_navigational(resource) { + redirect_to new_user_registration_path + } + end + end + def set_instance_presenter @instance_presenter = InstancePresenter.new end @@ -74,10 +82,37 @@ class Auth::RegistrationsController < Devise::RegistrationsController @body_classes = %w(edit update).include?(action_name) ? 'admin' : '' end - def set_challenge - @challenge_add_1 = rand(0...9) - @challenge_add_2 = rand(0...9) - session[:challenge_answer] = @challenge_add_1 + @challenge_add_2 + def set_challenge_buster + @challenge_buster = SecureRandom.hex + end + + def passed_challenge?(serverToken, userParams) + # Log if captcha keys not present in ENV + if ENV.fetch('GAB_CAPTCHA_CLIENT_KEY', '').empty? || ENV.fetch('GAB_CAPTCHA_CLIENT_KEY', '').nil? + Rails.logger.debug "RegistrationsController: GAB_CAPTCHA_CLIENT_KEY is undefined" + end + + # Log and return false is captcha key is not present. This will disallow anyone from signing up + if ENV.fetch('GAB_CAPTCHA_SECRET_KEY', '').empty? || ENV.fetch('GAB_CAPTCHA_SECRET_KEY', '').nil? + Rails.logger.debug "RegistrationsController: GAB_CAPTCHA_SECRET_KEY is undefined" + return false + end + + typedChallenge = userParams[:challenge] + username = userParams[:account_attributes][:username] + + return false if serverToken.nil? || serverToken.empty? || typedChallenge.nil? || typedChallenge.empty? + + Request.new(:post, "https://captcha.gab.com/captcha/#{serverToken}/verify", form: { + "serverKey" => ENV.fetch('GAB_CAPTCHA_SECRET_KEY', ''), + "value" => typedChallenge, + "username" => username, + "ip" => request.env['REMOTE_ADDR'] + }).perform do |res| + body = JSON.parse(res.body_with_limit) + result = !!body["success"] + return result + end end def determine_layout diff --git a/app/views/auth/registrations/new.html.haml b/app/views/auth/registrations/new.html.haml index 15e107f1..dba8fc88 100644 --- a/app/views/auth/registrations/new.html.haml +++ b/app/views/auth/registrations/new.html.haml @@ -5,7 +5,10 @@ = render partial: 'shared/og' = simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| - %h2.form-title Sign up for Gab + %div{style: "display:flex;flex-direction:row;height:36px;width:100%;align-items:center;margin-bottom:15px;"} + %h2.form-title{style: "padding:0;margin:0;"} Sign up for Gab + %div{style: "display:flex;width:90px;height:36px;margin-left:auto;margin-top:-10px;"} + = f.button :button, t('auth.register'), type: :submit, style: "height:36px;font-size:14px;" = render 'shared/error_messages', object: resource @@ -22,9 +25,12 @@ .fields-group = f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' } - .fields-group - = f.input :challenge, wrapper: :with_label, label: "Are you a human? What is #{@challenge_add_1} + #{@challenge_add_2} = ", required: true, input_html: { 'aria-label' => "Are you a human? What is #{@challenge_add_1} + #{@challenge_add_2}", :autocomplete => 'off' } - + .fields-group{style: "flex-direction:column;"} + = f.input :challenge, wrapper: :with_label, label: "Are you a human? Enter the text below.", required: true, input_html: { 'aria-label' => "Are you a human? Enter the text below.", :autocomplete => 'off' } + %span{style: "margin-top:5px;font-size:12px;color:red;"}= flash[:captcha_error] + %div#gab-captcha{style: "display:block;position:relative;width:240px;height:100px;margin-top:10px;border-radius:6px;overflow:hidden;border:1px solid #ccc;"} + %span{style:"display:block;position:absolute;line-height:100px;width:240px;height:100px;top:0;left:0;right:0;bottom:0;text-align:center;color:#ccc;"} • • • + .fields-group-agreement = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', about_tos_path: about_tos_path) @@ -32,3 +38,6 @@ = f.button :button, t('auth.register'), type: :submit .form-footer= render 'auth/shared/links' + + +%script{src: "https://captcha.gab.com/captcha/#{ENV.fetch('GAB_CAPTCHA_CLIENT_KEY', '')}/challenge.js?b=#{@challenge_buster}", type: "application/javascript" }