<p>We do this at Transform Studios at the DNS level by using cloudflare’s Web Application Firewall. You can basically add a rule which targets any url that matches the control panel login page and secure it with a captcha challenge before giving access to the page. I’ve found this is best to handle security before visitors even hit your site and more importantly prevents any core code base changes or creation of addons.</p>

CC BY-SA 4.0
2020-09-07T14:43:56.917Z
2020-09-07T14:43:56.917Z
Adnan Kussair
<p>As far as I know there is no "build in way" to achieve this. 
There is a way but it's really ugly and I would suggest you not to do what I write. Maybe someone can make it a little bit "cleaner" better or whatever but this code should actually not be used. It's only some kind of food for thought..</p>

<p>Ok so... you <strong>could</strong> inject a custom javascript code during the login process that adds a captcha to the login html...</p>

<pre><code>public function init(){
    parent::init();
    if(craft()-&gt;request-&gt;url === "/admin/login"){
        // build your captcha, store the correct one in your session and build your html here
        craft()-&gt;templates-&gt;includeJs(
            // after document ready, append your html to the form
            // $("#login-form").append(yourHtml); or something like that
        );
    }
}
</code></pre>

<p>Like I said, it's a but ugly but I was not able to find a "clean way". The other workaround would be adding your code directly into the login.html but as soon as this file is updated you would have to include your html again (or you would have to prevent updates at all)</p>

<p>Then you can use this hook <a href="https://craftcms.com/docs/plugins/events-reference#user-api-events" rel="nofollow noreferrer">Link</a> to check if the passed paramter matches yours. The following code is just an example. I didn't include any security checks. <strong>Don't use this as it is on your website</strong>.</p>

<pre><code>craft()-&gt;on('userSession.onLogin', function(Event $event){
    $captcha = craft()-&gt;request-&gt;getPost('yourCaptchaParam');
    if($captcha !== craft()-&gt;httpSession-&gt;get('captcha')){
        $event-&gt;performAction = false;
    }
    return false;
});
</code></pre>

<p><strong>Edit:</strong> ups sry - I saw too late you want to use the reCaptcha and not a captcha.. but the process would be the same</p>

<p>Edit2: 
This is the default craft login action. You can try to remove the default event handler from the button and attach your own in your init as well</p>

<pre><code>submitLogin: function()
{
    var data = {
        loginName: this.$loginNameInput.val(),
        password: this.$passwordInput.val(),
        rememberMe: (this.$rememberMeCheckbox.prop('checked') ? 'y' : '')
    };

    Craft.postActionRequest('users/login', data, $.proxy(function(response, textStatus)
    {
        if (textStatus == 'success')
        {
            if (response.success)
            {
                window.location.href = Craft.getUrl(response.returnUrl);
            }
            else
            {
                //since you have no ganish, just change that to a jquery method
                Garnish.shake(this.$form);
                this.onSubmitResponse();

                // Add the error message
                if (!error)
                {
                     error = Craft.t('An unknown error occurred.');
                }

                this.$error = $('&lt;p class="error" style="display:none"&gt;'+error+'&lt;/p&gt;').insertAfter($('.buttons', this.$form));
                this.$error.velocity('fadeIn');
            }
        }
        else
        {
            this.$submitBtn.removeClass('active');
            this.$spinner.addClass('hidden');
            this.loading = false;
        }

    }, this));

    return false;
},
</code></pre>

CC BY-SA 3.0
2017-10-23T23:17:08.133Z
Would I essentially be creating a simple Craft plugin that accomplishes these things? (Thanks for the detailed response!)
bbeecher
CC BY-SA 3.0
2017-10-24T04:34:45.030Z
Yes. You only need one file and this site pluginfactory.io can create it for you. I made around 25 sites with craft in the last few years and not a single one had no custom plugin. That's how craft is supposed to work
Robin Schambach
CC BY-SA 3.0
2017-10-26T22:39:12.867Z
Craft's login form actually submits via ajax `craft/app/resources/js/compressed/login.js` Adding Recaptcha to the admin login does not look possible without some extreme measures (modifying the `login.js` to include the g-recaptcha-response and only serving uncompressed js... alternatively creating a custom admin login) :(
bbeecher
CC BY-SA 3.0
2017-10-27T03:07:12.510Z
or you could try to remove the default event handler from the button and just include a custom ajax function to it. I copied crafts function in my answer, its not too complicated to change it, since it's quite simple. And by the way: building a custom login is really easy too. I had to made that for one project too. You just need to copy paste many things
Robin Schambach
CC BY-SA 3.0
2017-10-19T06:48:22.930Z
2017-10-27T03:05:06.510Z
2017-10-27T03:05:06.510Z
Robin Schambach
<p>Is there any plugin or relatively straightforward and accepted way to add a recaptcha to the login for the admin page?</p>

<p>My research so far has come up with nothing. I'm curious if others have looked into this with any result. </p>

CC BY-SA 3.0
2017-10-27T15:23:52.187Z
I would highly recommend not trying to do this. Your code could introduce other bugs into the system and allow an attacker to compromise it. If you're worried about bots brute forcing your install, for example, use the tools in Craft to rate limit them, change your CP login URL periodically,  and consider implementing rate limiting on the server side as well (nginx has rate limiting built-in for example).
RitterKnight
CC BY-SA 3.0
2017-11-03T16:27:00.047Z
Thanks @RitterKnight, I see now it is probably both unnecessary and not a great idea
bbeecher
<login><security>
Adding reCAPTCHA to the admin login page
# Adding reCAPTCHA to the admin login page

(Asked by: username_0 on 2017-10-19)

Is there any plugin or relatively straightforward and accepted way to add a recaptcha to the login for the admin page?

My research so far has come up with nothing. I'm curious if others have looked into this with any result.

**Tags:** login, security

> **Comments:**
> **username_1**: I would highly recommend not trying to do this. Your code could introduce other bugs into the system and allow an attacker to compromise it. If you're worried about bots brute forcing your install, for example, use the tools in Craft to rate limit them, change your CP login URL periodically,  and consider implementing rate limiting on the server side as well (nginx has rate limiting built-in for example).  
> **username_0**: Thanks @username_1, I see now it is probably both unnecessary and not a great idea  

---

## 2 answers

### Answer by username_2 on 2020-09-07. Score: 2

We do this at Transform Studios at the DNS level by using cloudflare’s Web Application Firewall. You can basically add a rule which targets any url that matches the control panel login page and secure it with a captcha challenge before giving access to the page. I’ve found this is best to handle security before visitors even hit your site and more importantly prevents any core code base changes or creation of addons.


---

### Answer by username_3 on 2017-10-19. Score: 1

As far as I know there is no "build in way" to achieve this. 
There is a way but it's really ugly and I would suggest you not to do what I write. Maybe someone can make it a little bit "cleaner" better or whatever but this code should actually not be used. It's only some kind of food for thought..
Ok so... you could inject a custom javascript code during the login process that adds a captcha to the login html...
public function init(){
    parent::init();
    if(craft()->request->url === "/admin/login"){
        // build your captcha, store the correct one in your session and build your html here
        craft()->templates->includeJs(
            // after document ready, append your html to the form
            // $("#login-form").append(yourHtml); or something like that
        );
    }
}

Like I said, it's a but ugly but I was not able to find a "clean way". The other workaround would be adding your code directly into the login.html but as soon as this file is updated you would have to include your html again (or you would have to prevent updates at all)
Then you can use this hook Link to check if the passed paramter matches yours. The following code is just an example. I didn't include any security checks. Don't use this as it is on your website.
craft()->on('userSession.onLogin', function(Event $event){
    $captcha = craft()->request->getPost('yourCaptchaParam');
    if($captcha !== craft()->httpSession->get('captcha')){
        $event->performAction = false;
    }
    return false;
});

Edit: ups sry - I saw too late you want to use the reCaptcha and not a captcha.. but the process would be the same
Edit2: 
This is the default craft login action. You can try to remove the default event handler from the button and attach your own in your init as well
submitLogin: function()
{
    var data = {
        loginName: this.$loginNameInput.val(),
        password: this.$passwordInput.val(),
        rememberMe: (this.$rememberMeCheckbox.prop('checked') ? 'y' : '')
    };

    Craft.postActionRequest('users/login', data, $.proxy(function(response, textStatus)
    {
        if (textStatus == 'success')
        {
            if (response.success)
            {
                window.location.href = Craft.getUrl(response.returnUrl);
            }
            else
            {
                //since you have no ganish, just change that to a jquery method
                Garnish.shake(this.$form);
                this.onSubmitResponse();

                // Add the error message
                if (!error)
                {
                     error = Craft.t('An unknown error occurred.');
                }

                this.$error = $(''+error+'').insertAfter($('.buttons', this.$form));
                this.$error.velocity('fadeIn');
            }
        }
        else
        {
            this.$submitBtn.removeClass('active');
            this.$spinner.addClass('hidden');
            this.loading = false;
        }

    }, this));

    return false;
},

> **Comments:**
> **username_0**: Would I essentially be creating a simple Craft plugin that accomplishes these things? (Thanks for the detailed response!)  
> **username_3**: Yes. You only need one file and this site pluginfactory.io can create it for you. I made around 25 sites with craft in the last few years and not a single one had no custom plugin. That's how craft is supposed to work  
> **username_0**: Craft's login form actually submits via ajax `craft/app/resources/js/compressed/login.js` Adding Recaptcha to the admin login does not look possible without some extreme measures (modifying the `login.js` to include the g-recaptcha-response and only serving uncompressed js... alternatively creating a custom admin login) :(  
> **username_3**: or you could try to remove the default event handler from the button and just include a custom ajax function to it. I copied crafts function in my answer, its not too complicated to change it, since it's quite simple. And by the way: building a custom login is really easy too. I had to made that for one project too. You just need to copy paste many things  

---

