ReCAPTCHA v3 Form Hidden Field Not Populating | Community
Skip to main content
bellis0
New Participant
July 26, 2021
Question

ReCAPTCHA v3 Form Hidden Field Not Populating

  • July 26, 2021
  • 1 reply
  • 6295 views

Hi,

I'm experiencing inconsistencies with getting Google ReCAPTCHA v3 user score to populate in a hidden field when a form is submitted. The score is added correctly to the hidden field most of the time but is empty roughly 10-15% of the time (based on our lead submissions this month). I've followed the recommended Marketo implementation of recaptcha and will place the current implementation below. The implementation below is used on all of our pages that include Marketo forms, and I we have seen issues on all of them (not finding any trends that would lead me to believe it's a specific page-related issue). NOTE: We are NOT using Marketo for the validation process - this is built on Adobe Runtime, and as you can see is called inside the grecaptcha.execute function after the recaptchaFinger is returned (in .then()). Also, we only recently changed the captchaResponse field (the hidden field that is added with recaptcha score) to be readOnly, so this shouldn't be an issue.

 

If anyone has any thoughts or tips to resolve this it would be greatly appreciated! Thanks!

// if Marketo Forms function available, and document contains a marketo form if (typeof MktoForms2 !== "undefined" && document.querySelectorAll("form[id^=\"mktoForm_\"]").length > 0) { /* ======= START RECAPTCHA ======= */ // user config for recaptcha var userConfig = { apiKeys : { recaptcha : <REMOVED_FOR_PRIVACY> }, fields : { recaptchaFinger : "lastRecaptchaUserInput" }, actions : { formSubmit : "form" } }; /* inject the reCAPTCHA library */ recaptchaLib = document.createElement("script"); recaptchaLib.src="https://www.google.com/recaptcha/api.js?render=" + userConfig.apiKeys.recaptcha + "&onload=grecaptchaListeners_ready"; document.head.appendChild(recaptchaLib); MktoForms2.whenReady(function(mktoForm) { var formEl = mktoForm.getFormElem()[0], submitButtonEl = formEl.querySelector("button[type='submit']"), moveEl = formEl.querySelector('.google-recaptcha-disclaimer'); // move disclaimer text row below submit button [].forEach.call(formEl.querySelectorAll('.mktoForm > .mktoFormRow'), function(row) { !row.contains(moveEl) || formEl.appendChild(row); }); /* pending reCAPTCHA widget ready */ submitButtonEl.disabled = true; /* pending reCAPTCHA verify */ mktoForm.submittable(false); mktoForm.locked = false; mktoForm.onValidate(function(native) { if (!native) return; // generate recaptcha token grecaptcha.ready(function() { grecaptcha.execute(userConfig.apiKeys.recaptcha, { action: userConfig.actions.formSubmit }) .then(function(recaptchaFinger) { // ===================================== // Validation built on Adobe I/O Runtime // ===================================== fetch('https://adobeioruntime.net/api/v1/web/23476-166aquamarinetoucan/default/validate.json?token='+ recaptchaFinger) .then(function(response) { return response.json(); }) .then(function(data) { // access to response object here: score, success, challenge_ts, hostname _satellite.logger.log('recieved captcha data') var mktoFields = {}; // log errors, add errors to mktofield if (data["error-codes"]) { _satellite.logger.error('Error(s) validating recaptcha: ', data["error-codes"]); _satellite.setVar('recaptchaError', data["error-codes"].join('|')); mktoFields["recaptchaError"] = data["error-codes"].join('|'); } if (mktoForm.locked == false) { mktoForm.locked = true; // set hidden fields with response data OR error mktoFields["captchaResponse"] = data.score || data["error-codes"].join('|'); mktoForm.addHiddenFields(mktoFields); _satellite.logger.log('hidden fields added: ', document.querySelector('input[name=captchaResponse]')) document.querySelector('input[name=captchaResponse]').readOnly = true; _satellite.logger.log('in recaptcha fetch making form submittable, submitting'); // submit the form mktoForm.submittable(true); mktoForm.submit(); _satellite.logger.log('FORM SUBMITTING (in recaptcha tag)', _satellite.getVar('recaptchaResponse')) } }) .catch(function(err) { _satellite.logger.error('Error getting recaptcha response from Adobe Runtime API: ', err) }); }); }); }); }); var recaptchaListeners = { ready : function() { MktoForms2.whenReady(function(mktoForm){ var formEl = mktoForm.getFormElem()[0], submitButtonEl = formEl.querySelector("button[type='submit']"); submitButtonEl.disabled = false; }); } }; Object.keys(recaptchaListeners).forEach(function globalize(fnName){ window["grecaptchaListeners_" + fnName] = recaptchaListeners[fnName]; }); /* ======== END RECAPTCHA ======== */ }

 

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.

1 reply

SanfordWhiteman
New Participant
July 26, 2021

I've followed the recommended Marketo implementation of recaptcha and will place the current implementation below.

? This is certainly not the recommended implementation because you can trivially work around the reCAPTCHA!

 

The whole idea of reCAPTCHA is the validation occurs outside of the user’s control.  What you’ve posted here allows the end user to spoof a passed/high-confidence reCAPTCHA.

bellis0
bellis0Author
New Participant
July 27, 2021

Hi Sanford, I'll provide a little more context and please let me know if you have any recommendations. 

We originally used Marketo for our validation process - passed in the recaptcha token generated by grecaptcha.execute into a hidden field and then handled the validation once inside Marketo. This led to inconsistencies as well, where empty values were getting passed into that hidden field (not sure why, but I saw another community post explaining that the validation process on Marketo is also not fool proof).

 

The other thing to mention is we have multiple places that we want to share the response from the recaptcha (Adobe Analytics, paid media gtags, etc that fire on form submit) so it helps to have access to the response on the front end. I do not believe this would be possible if we did Marketo validation. We are also not blocking submissions on the front end, we are passing in the score to Marketo and then handling the cleaning process of those leads from that point. 

 

Could you elaborate on how a bot would be able to tap into the current process? I imagine we could set up a CORs policy to block usage of that API endpoint from outside sources. Aside from the fact that the API endpoint is exposed, I can't identify any timing issues.. the response from the validation is returned before the value is added to the hidden field and the form is submitted. Any ideas?

SanfordWhiteman
New Participant
July 27, 2021

This led to inconsistencies as well, where empty values were getting passed into that hidden field (not sure why, but I saw another community post explaining that the validation process on Marketo is also not fool proof).

Absolutely no reason for this to happen in a proper setup. The population of the user fingerprint (client-side “response”) field is a matter of setting correctly it via JS.

 


Could you elaborate on how a bot would be able to tap into the current process? I imagine we could set up a CORs policy to block usage of that API endpoint from outside sources. Aside from the fact that the API endpoint is exposed, I can't identify any timing issues.. the response from the validation is returned before the value is added to the hidden field and the form is submitted. Any ideas?

Has nothing to do with CORS. I can trivially add whatever captchaResponse value I want to the form data and submit it, totally bypassing the reCAPTCHA.

 

The server-side response from Google must be retrieved on the server. You can’t let the client send it, that’s kind of the main idea.