12

Is possible to customize reCAPTCHA form parameters (recaptcha_challenge_field and recaptcha_response_field) so that they are called differently?

Basically I want the form parameter recaptcha_challenge_field to be called captchaId, and recaptcha_response_field to be called captchaUserResponse.

I want them renamed so I can abstract the captcha implementation... when a request arrives on

POST /mysite/userSignup

I don't want to bother with captcha implementation (reCaptcha, or something else in the future) - extracting the right parameters for the right captcha implementation, I want to unify those parameter names.

Now my request looks like this:

POST /mysite/userSignup HTTP/1.1
Host: localhost:80
Connection: keep-alive
Content-Length: 416
Cache-Control: max-age=0
Origin: http://localhost:80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://localhost:8080/mysite/signup/form.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,hr;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

email:testUser%40gmail.com
username:testUser
password:test
password2:test
forename:Test
surname:User
recaptcha_challenge_field:<google generated challange>
recaptcha_response_field:<user typed captcha answer>
submit:Submit

But I want it to look like this:

POST /mysite/userSignup HTTP/1.1
Host: localhost:80
Connection: keep-alive
Content-Length: 416
Cache-Control: max-age=0
Origin: http://localhost:80
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.46 Safari/536.5
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Referer: http://localhost:8080/mysite/signup/form.html
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8,hr;q=0.6
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

email:testUser%40gmail.com
username:testUser
password:test
password2:test
forename:Test
surname:User
captchaId:<google generated challange>
captchaUserResponse:<user typed captcha answer>
submit:Submit

An elegant way would be to specify those form parameter names like this:

<script>
   var RecaptchaOptions = {
      recaptcha_challenge_field_formparam_name : 'captchaId',
      recaptcha_response_field_formparam_name: 'captchaUserResponse'
   };
</script>

If this isn't possible, what workaround do you suggest?

davorp
  • 4,156
  • 3
  • 26
  • 34
  • 2
    I've asked the same question here: https://groups.google.com/forum/?hl=en&fromgroups#!topic/recaptcha/HlKhdgpLVjw – davorp Jun 01 '12 at 13:11

6 Answers6

3

It should not be possible. Probable solution, AFAIU, is the addition of new DOM elements which copy the values of the original ones right before the form submission, for example:

<!-- the HTML part -->
<input type="hidden" id="captchaId">
<input type="hidden" id="captchaUserResponse">

// the JavaScript (jQuery) part
$('form').submit(function(){
  $('input#captchaId').
    val($('input#recaptcha_response_field').val());
  $('input#captchaUserResponse').
    val($('input#recaptcha_challenge_field').val());
  return true;
})
Vidul
  • 10,128
  • 2
  • 18
  • 20
3

There is no need for extra fields. All you have to do is to IDentify the form elements with an id and change their relative name before the submit.

Assuming you have the following form:

<div id="captcha" class="item recaptcha">
...
<textarea id=custom1 name="recaptcha_challenge_field" rows="3" cols="40"></textarea>
<input id=custom2 name="recaptcha_response_field" value="manual_challenge" type="hidden" />
<input type="submit" name="submit" value="Submit">
</div>

you can change names using

document.captcha.custom1.name='captchaId'
document.captcha.custom2.name='captchaUserResponse'

These changes can be included in the submit button directly or indirectly (call another method) using the onclick event.

So for direct changes your submit will be:

<input type="submit" name="submit" value="Submit" onclick="document.captcha.custom1.name='captchaId';document.captcha.custom2.name='captchaUserResponse';return true">
  • that's ok, but since I'm not building a single sign-up form for a single site, this has to be as easly embeddable as possible, without extra client interventions. For example:
    – davorp Jun 08 '12 at 11:28
  • recaptcha has its own instance rules that cannot be changed or adapted to your abstraction idea. As a result you have to deal with client interventions as proposed by Vidul or deal with my proposal which is server oriented (html is generated on server and without the need for external JS libraries). So for recaptcha your server code may have a routine that accepts the form action as a parameter and generates the relative html code. For other captcha implementations alter the routine to accept also the implementation as a paramener and generate html using switch or if statements. –  Jun 08 '12 at 13:35
  • These are the problems of your answer: (1) You're using ID for referencing which the original form may not have such attribute. (2) You're using onclick event which is not a suitable event for this, because a form can be submitted by TAB and ENETER keys combination. – Sepehr Jun 11 '12 at 06:11
3

Since the OP has not accepted any answers yet, I'm providing this one. It's just as the exact thing the OP is looking for in the comments of this post: one of the answers

create a PHP script and put it somewhere. I assume that the URI of the created page is DESIRED_URI. place this code inside it:

<?php
    header("Content-type: text/javascript");
    /*
    * These are the script parameters
    * You can change them by including desired ones in script's URL (as GET params)
    * challenge_field_original: original name of captcha challenge field
    * response_field_original: original name of captcha response field
    * to_challenge_field: the name of the challenge field you want
    * to_response_field: the name of the response field you want
    */
    $params = array(
        /*
        * Defining the defaults of the parameters
        */
        "challenge_field_original" => "recaptcha_challenge_field",
        "response_field_original" => "recaptcha_response_field",
        "to_challenge_field" => "captchaId",
        "to_response_field" => "captchaUserResponse",
    );
    /*
    * Checking for passed GET params to re-fill the parameters with non-default ones (if any)
    */
    if(isset($_GET['challenge']) && $_GET['challenge'] != "") {
        $params["to_challenge_field"] = $_GET['challenge'];
    }
    if(isset($_GET['response']) && $_GET['response'] != "") {
        $params["to_response_field"] = $_GET['response'];
    }
    if(isset($_GET['challenge_original']) && $_GET['challenge_original'] != "") {
        $params["challenge_field_original"] = $_GET['challenge_original'];
    }
    if(isset($_GET['response_original']) && $_GET['response_original'] != "") {
        $params["response_field_original"] = $_GET['response_original'];
    }
?>
/*
* I'm going to find the index of this script tag in the whole document
* So I can find its previous sibling node (the captcha form node)
*/
var scripts = document.getElementsByTagName( 'script' );
var thisScriptTag = scripts[ scripts.length - 1 ];

//now we have the index, good, I'm going to find its previous sibling node
var targetFormTag = (function(element) {
    if(typeof(element.previousElementSibling) == "undefined") {
        var p = element;
        do p = p.previousSibling;
        while (p && p.nodeType != 1);
        return p;
    } else {
        return element.previousElementSibling;
    }
})(thisScriptTag);

//Changing names...
(function(targetForm) {
    var a = targetForm.getElementsByTagName('input');
    var attr = '';
    var toAttr = '';
    for (var i in a) {
        try {
            attr = a[i].getAttribute('name')
        } catch(e) {
            continue;
        }
        switch(attr) {
            case '<?php echo $params["challenge_field_original"]; ?>':
            {toAttr='<?php echo $params["to_challenge_field"]; ?>';break;}
            case '<?php echo $params["response_field_original"]; ?>':
            {toAttr='<?php echo $params["to_response_field"]; ?>';break;}
        }
        a[i].setAttribute('name',toAttr);
    }
})(targetFormTag)

Usage:

As simple as including the script right after the form (!this is very important!). like this:

<form><!--form elements--></form>
<script type="text/javascript" src="DESIRED_URI"></script>

You may change the name of the fields after a while, for that, read code's documentation.

Side-notes:

  • The script is ridiculously cross-browser (IE5+, Chrome, FF, Android)
  • You may use this for any captcha script, just supply the name of the original input and the desired name of that input field in the script's src.
  • The script tries to change the fields, on failure, it won't raise any errors
Community
  • 1
  • 1
Sepehr
  • 2,051
  • 19
  • 29
  • I already did the parameter name mapping on the server side. I suppose reCAPTCHA doesn't support custom form parameter names - so the server side solution is the cleanest. – davorp Jun 12 '12 at 09:46
  • 1
    @davorp I really wanted to help and these answers are all I know, wish you the bests ;D – Sepehr Jun 12 '12 at 13:04
2

Here you go, I was in the same situation with you, and this is how I made it:

Short version:
here, add this attribute to your form, this is all you need:

EDIT2 - added pure Javascript code:

pure-Javascript approach
Tested on IE8, FF12 and Chrome:

onsubmit="javascript:(function(p){var a=p.getElementsByTagName('input');var attr='';var toAttr='';for (var i in a){try {attr=a[i].getAttribute('name')} catch(e) {continue;}switch(attr){case 'recaptcha_challenge_field':{toAttr='captchaId';break;}case 'recaptcha_response_field':{toAttr='captchaUserResponse';break;}}a[i].setAttribute('name',toAttr);}p.submit();})(this)"

jQuery approach:

onsubmit="javascript:(function(p){var f=$(p);$('input[name=recaptcha_challenge_field]',f).attr('name','captchaId');$('input[name=recaptcha_response_field]',f).attr('name','captchaUserResponse');f.submit();})(this)"

E.G:

<form method="post" action="verify.php" onsubmit="javascript:(function(p){var f=$(p);$('input[name=recaptcha_challenge_field]',f).attr('name','captchaId');$('input[name=recaptcha_response_field]',f).attr('name','captchaUserResponse');f.submit();})(this)">
    <?php
    require_once('recaptchalib.php');
    $publickey = "your_public_key"; // you got this from the signup page
    echo recaptcha_get_html($publickey);
    ?>
    <input type="submit" />
</form>

Long version:
Here is what I'm doing:

1- This is the actual javascript function which is in charge of replacing names of inputs, nothing special here:

function(param){
    var form = $(param);
    $('input[name=recaptcha_challenge_field]',form).attr('name','captchaId');
    $('input[name=recaptcha_response_field]',form).attr('name','captchaUserResponse');
    form.submit();
}

2- I'll make that mentioned function, an anonymous function so I can call it via inline HTML events (onsubmit event):

(function(param){...function code...})(this)
  • Noticed that (this)? at the end of our anonymous function? by this, I can pass a handle of our form to the function.

3- Since you need as little as possible client modification (or intervention you said?), I'll call this anonymous function from an HTML inline event handler. in our case, the best handler is onsubmit.

Well, honestly, I enjoyed this solution myself, hope it comes useful for you too :D

EDIT1:

By the way, I haven't noticed your comments, if you want to do it in this way:

<form><!--blah blah--></form>
<script type="text/js" src="somewhere"></script>

These are the cons:

  1. You're adding an extra HTTP request!! why?
  2. You're making it asynchronous from form's load-complete event which makes it inaccurate
  3. Have you ever thought that maybe your webserver, at that specific time, rejects your client's request? so what happens? it breaks your server side script!
  4. If you're printing form, server-side, why you don't make these changes, server-side?
Sepehr
  • 2,051
  • 19
  • 29
  • You're assuming he's using jQuery. That might not be the case. – Madara's Ghost Jun 09 '12 at 11:16
  • 1
    Well, he is free to ignore using of jQuery! his call, I'm just providing a solution, well, yeah, and it shows that I'm overly addicted to jQuery :( – Sepehr Jun 09 '12 at 11:22
  • 2
    @Truth Oh, My, GOD! I've never seen that picture before! that made my day :D I'm going to edit my reply for a jQuery-less approach. thanks Truth. – Sepehr Jun 09 '12 at 11:26
  • Pure javascript added, now I feel happy with that pure code :D – Sepehr Jun 09 '12 at 11:58
  • 3p3r, how do you know that davorp uses php? In your example you are using 'recaptchalib.php' witch creates the form (html) content by calling the function recaptcha_get_html. (a) This is what I described in my answer without mentioning http://code.google.com/p/recaptcha/ (b) You changed my proposal for ids in the form fields to direct attribute alteration. (c) Finally since 'recaptchalib.php' is local the whole form generation should occur with only one call to recaptcha_get_html modified to accept a form name and an action as parameters. Sorry but -1 from me for your answer authenticity. –  Jun 10 '12 at 22:43
  • @drdigit (a) first of all, it doesn't matter what library are you using since you are the one who designs the actual wrapping form of the reCaptcha. (b) what? I changed your proposal? it's not rocket science man! I made a functional bookmarklet which gets triggered by onsubmit event. you used ID and onclick event (which is 100% wrong since a form can be submitted by TAB and ENTER keys combination). so your -1 is completely useless. – Sepehr Jun 11 '12 at 06:08
2

You want a generic way of dealing with captchas, so that down the road, if you decide to use something other than reCAPTCHA, you can deal with it the same way. I think that your thinking is great, but you are abstracting too deeply.

Ideally, you want some black box captcha system that just does its thang and you don't have to worry about which captcha plugin is in the box. The only thing you need to get out of that box is whether or not the user entered the right thing.

If you try to dictate HOW the black box does this check, you are mucking with the box. Furthermore, if you do end up switching to a different captcha plugin, you will have to muck with that one as well.

For these reasons, I suggest that you only abstract out one thing: a is_human_user method (or whatever you want to call it). All you would need to do is implement this method for each captcha plugin that you decide to use. For reCAPTCHA, it sounds like this method would return recaptcha_challenge_field == recaptcha_response_field. Then, whatever does the checking just calls is_human_user and is blind to what the implementation is.

I'm sorry if this is not the answer you are looking for but it is always important to step back and remind yourself why you are down that rabbit hole.

lbstr
  • 2,822
  • 18
  • 29
1

I believe you can't abstract too much a captcha implementation, because every captcha uses it's own validate function, anyway changing the name of the captcha fields in the clients side it's not the best solution, because if the client doesn't have JavaScript enabled it simply won't work.

Considering this, i believe the best way to abstract the name of the fields is by using variables in the server side, after you receive the post of the form, you can do:

$captchaId = $_POST["recaptcha_challenge_field"];
$captchaUserResponse = $_POST["recaptcha_response_field"];

Then pass this to a generic validate function like this:

recaptcha_generic_check ("PrivateKey", $_SERVER["REMOTE_ADDR"], $captchaId, $captchaUserResponse);

Ideally, this way you only will need to change the name of the fields received in the post (and the function of course), and everything will remain the same.

Rafael
  • 2,827
  • 1
  • 16
  • 17
  • This is basicly what 3p3r suggested (http://stackoverflow.com/a/10982928/41939), but 9 hours later. :) – davorp Jun 12 '12 at 09:50
  • Well no, 3p3r suggest doing a php script to rename the fields in the browser with JavaScript, i suggest to simply assign the incoming fields in the post to a variable and pass that to the captcha validation function. I believe i missed that you want to provide a sign on form for multiple sites, anyway i believe this approach can help you, because you may know by the incoming captcha fields what captcha solution is using the client, while with 3p3r approach you may lose that info unlesss you specify it with another parameter. – Rafael Jun 12 '12 at 15:28
  • you're looking to his older answer, the newer one is proposing the server side mapping. There is no client side renaming, just wrapping-up the recaptcha javascript with my own server-side-generated javascript: – davorp Jun 12 '12 at 15:39