95 可读性
 将光标移到/点击文章中的句子上,可以查看译文。      显示繁体中文内容    显示简体中文内容

reCAPTCHA custom form parameter names
reCAPTCHA自定义表单参数名称

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?

时间:原作者:7个回答

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;
})
原作者:

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 ( onsubmitevent):

(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?
原作者:

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
原作者:

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">
原作者:

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.

原作者:

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_usermethod (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_userand 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.

原作者:
...