首页 文章

ReCaptcha在iPhone上无法正常工作

提问于
浏览
40

我有一个简单的联系表格的网站 . 验证有点微小,因为它不会进入数据库;只是一封电子邮件表格如下:

有5个字段 - 其中4个是必需的 . 提交被禁用,直到4个字段有效,然后您可以提交 . 然后在服务器上再次验证所有内容,包括recaptcha(我的客户端未验证) . 整个过程使用ajax完成,并且有多个测试必须在服务器端传递或返回4 **头,并调用失败回调处理程序 .

一切都像桌面上的Chrome浏览器一样(我没有尝试过其他浏览器,但我无法想象他们为什么会有所不同),但是在iPhone上,即使我没有选中复选框,reCaptcha仍会验证考试 .

换句话说:我仍然需要正确填写四个值才能提交,但如果我没有选中reCaptcha的方框,请求仍然会成功 .

我可以发布一些代码,如果有人认为这将有所帮助,但似乎问题在于设备而不是代码 . 有没有人对此有任何见解?


注意:如果这有用,服务器端是PHP / Apache .


Update: 5/28/2015

我还在调试这个,但似乎Mobile Safari忽略了我的iPhone上的响应 Headers . 当我将响应输出到页面时,我在桌面上获得的 (data,status,xhr) 是:

  • data :我的回答在这一点上只是说错误或成功 - > error

  • statuserror

  • xhr{'error',400,'error'}

在移动野生动物园:

  • dataerror

  • statussuccess

  • xhr{'error',200,'success'}

所以 - 它似乎只是忽略了我的响应头 . 我试着明确设置 {"headers":{"cache-control":"no-cache"}} 但无济于事 .


Update: 6/3/2015

每个请求,这是代码 . 这几乎可以肯定超过你的需要 . 由于我试图修复它所做的改变,它也变得更加迟钝 . 另请注意,虽然可能看起来存在尚未定义的变量,但它们(应该)已在其他文件中定义 .

The client side

$('#submit').on('click', function(e) {

    $(this).parents('form').find('input').each(function() {
        $(this).trigger('blur');
    })
    var $btn = $(this);
    $btn = $btn.button('loading');
    var dfr = $.Deferred();

    if ($(this).attr('disabled') || $(this).hasClass('disabled')) {

        e.preventDefault();
        e.stopImmediatePropagation();
        dfr.reject();
        return false;

    } else {

        var input = $('form').serializeArray();
        var obj = {},
            j;

        $.each(input, function(i, a) {

            if (a.name === 'person-name') {

                obj.name = a.value;

            } else if (a.name === 'company-name') {

                obj.company_name = a.value;

            } else {

                j = a.name.replace(/(g-)(.*)(-response)/g, '$2');
                obj[j] = a.value;

            }

        });

        obj.action = 'recaptcha-js';
        obj.remoteIp = rc.remoteiP;
        rc.data = obj;

        var request = $.ajax({

            url: rc.ajaxurl,
            type: 'post',
            data: obj,

            headers: {
                'cache-control': 'no-cache'
            }

        });

        var success = function(data) {

            $btn.data('loadingText', 'Success');
            $btn.button('reset');
            $('#submit').addClass('btn-success').removeClass('btn-default');
            $btn.button('loading');
            dfr.resolve(data);


        };
        var fail = function(data) {

            var reason = JSON.parse(data.responseText).reason;
            $btn.delay(1000).button('reset');
            switch (reason) {

                case 'Recaptcha Failed':
                case 'Recaptcha Not Checked':
                case 'One Or more validator fields not valid or not filled out':
                case 'One Or more validator fields is invalid':

                    // reset recaptcha

                    if ($('#submit').data('tries')) {

                        $('#submit').remove();
                        $('.g-recaptcha').parent().addBack().remove();

                        myPopover('Your request is invalid.  Please reload the page to try again.');

                    } else {

                        $('#submit').data('tries', 1);
                        grecaptcha.reset();

                        myPopover('One or more of your entries are invalid.  Please make corrections and try again.');
                    }


                    break;

                default:

                    // reset page
                    $('#submit').remove();
                    $('.g-recaptcha').remove();


                    myPopover('There was a problem with your request.  Please reload the page and try again.');

                    break;
            }
            dfr.reject(data);

        };

        request.done(success);
        request.fail(fail);



    }

The Server:

function _send_email(){

$recaptcha=false;
/* * */
if(isset($_POST['recaptcha'])):

    $gRecaptchaResponse=$_POST['recaptcha'];
    $remoteIp=isset($_POST['remoteIp']) ? $_POST['remoteIp'] : false;

    /* ** */
    if(!$remoteIp):

        $response=array('status_code'=>'409','reason'=>'remoteIP not set');
        echo json_encode($response);
        http_response_code(409);

        exit();

    endif;
    /* ** */

    /* ** */
    if($gRecaptchaResponse==''):

        $response=array('status_code'=>'400','reason'=>'Recaptcha Failed');
        echo json_encode($response);
        http_response_code(400);
        exit();

    endif;
    /* ** */

    if($recaptcha=recaptcha_test($gRecaptchaResponse,$remoteIp)):

        $recaptcha=true;

    /* ** */
    else:

        $response=array('status_code'=>'400','reason'=>'Recaptcha Failed');
        echo json_encode($response);
        http_response_code(400);
        exit();

    endif;
    /* ** */

/* * */
else:

    $response=array('status_code'=>'400','reason'=>'Recaptcha Not Checked');
    echo json_encode($response);
    http_response_code(400);
    exit();

endif;
/* * */

/* * */
if($recaptcha==1):

    $name=isset($_POST['name']) ? $_POST['name'] : false;
    $company_name=isset($_POST['company_name']) ? $_POST['company_name'] : false;
    $phone=isset($_POST['phone']) ? $_POST['phone'] : false;
    $email=isset($_POST['email']) ? $_POST['email'] : false;

    /* ** */
    if(isset($_POST['questions'])):

        $questions=$_POST['questions']=='' ? 1 : $_POST['questions'];

        /* *** */

    if(!$questions=filter_var($questions,FILTER_SANITIZE_SPECIAL_CHARS)):

         $response=array('status_code'=>'400','reason'=>'$questions could not be sanitized');
         echo json_encode($response);
         http_response_code(400);
         exit();

        endif;
       /* *** */

    /* ** */
    else:

      $questions=true;

    endif;
    /* ** */

    /* ** */
    if( count( array_filter( array( $name,$company_name,$phone,$email ),"filter_false" ) ) !=4 ):

        $response=array('status_code'=>'400','reason'=>'One Or more validator fields not valid or not filled out');
        echo json_encode($response);
        http_response_code(400);
        exit();

    endif;
    /* ** */

    $company_name=filter_var($company_name,FILTER_SANITIZE_SPECIAL_CHARS);
    $name=filter_var($name,FILTER_SANITIZE_SPECIAL_CHARS);
    $phone=preg_replace('/[^0-9+-]/', '', $phone);
    $email=filter_var($email,FILTER_VALIDATE_EMAIL);

    /* ** */
    if($company_name && $recaptcha && $name && $phone && $email && $questions):

        $phone_str='Phone:  ' . $phone;
        $company_str='Company:   ' . $company_name;
        $email_str='Email String:  ' . $email;
        $name_str='Name:  '.$name;
        $questions=$questions==1 ? '' : $questions;
        $body="$name_str\r\n\r\n$company_str\r\n\r\n$email_str\r\n\r\n$phone_str\r\n\r\n____________________\r\n\r\n$questions";


        $mymail='fake@fake.com';
        $headers   = array();
        $headers[] = "MIME-Version: 1.0";
        $headers[] = "Content-type: text/plain; charset=\"utf-8\"";
        $headers[] = "From: $email";
        $headers[] = "X-Mailer: PHP/" . phpversion();

        /* *** */
        if(mail('$mymail', 'Information Request from: ' . $name,$body,implode("\r\n",$headers))):

            $response=array('status_code'=>'200','reason'=>'Sent !');
            echo json_encode($response);
            http_response_code(200);
            exit();

        /* *** */
        else:

            $response=array('status_code'=>'400','reason'=>'One Or more validator fields is invalid');
            echo json_encode($response);
            http_response_code(400);
            exit();

        endif;
        /* *** */

     endif;
    /* ** */

   endif;
  /* * */

     $response=array('status_code'=>'412','reason'=>'There was an unknown error');
     echo json_encode($response);
     http_response_code(412);
     exit();
 }


function recaptcha_test($gRecaptchaResponse,$remoteIp){

    $secret=$itsasecret; //removed for security;

    require TEMPLATE_DIR . '/includes/lib/recaptcha/src/autoload.php';
    $recaptcha = new \ReCaptcha\ReCaptcha($secret);
    $resp = $recaptcha->verify($gRecaptchaResponse, $remoteIp);

    if ($resp->isSuccess()) {
        return true;
            // verified!
    } else {
        $errors = $resp->getErrorCodes();
        return false;
    }
 }

4 回答

  • 1

    就像那个问题iOS: Authentication using XMLHttpRequest - Handling 401 reponse最简单的解决方法是忽略自然 Headers 验证,并在回调成功时,用一些标志验证 .

    我看过一些这样的案例,从来没有闻到过好的 .

  • 0

    您的“remoteIP”变量是否在客户端正确设置?

    即使您的Ajax请求发送空值或假值,php脚本中的isset()函数也将返回true,从而错误地填充$ remoteIp .

    尝试做:

    $remoteIp = $_SERVER['REMOTE_ADDR'];
    

    Ajax只是让浏览器执行请求,因此PHP可以完美地获取用户的ip .

    我敢肯定,如果你传递错误的 Value ,ReCaptcha会以这种或那种方式陷入困境 .

    从不信任任何Javascript变量也比Ajax更安全,因为那些也应该被视为用户输入 .

  • 0

    验证码旨在防止恶意客户端(机器人),所以理论上 if a client bypasses the captcha, it is a server side problem. (但是,如果客户端无法完成验证码,则可能是服务器端问题或客户端问题 . )

    所以问题必须在服务器上 . 即使出于安全考虑,您应该使用 $_SERVER['REMOTE_ADDR'] 而不是 $_POST['remoteIp'] ,因为 $_POST['remoteIp'] 可能是伪造的(由恶意客户端) . 事实上, $_SERVER['REMOTE_ADDR'] 比客户端 $_POST['remoteIp'] 更可靠 .

  • 0

    我在2或3个月前创建了一个仍然完美的脚本,试试这个:

    <?php
    $siteKey = ''; // Public Key
    $secret = ''; // Private Key
    /**
     * This is a PHP library that handles calling reCAPTCHA.
     *    - Documentation and latest version
     *          https://developers.google.com/recaptcha/docs/php
     *    - Get a reCAPTCHA API Key
     *          https://www.google.com/recaptcha/admin/create
     *    - Discussion group
     *          http://groups.google.com/group/recaptcha
     *
     * @copyright Copyright (c) 2014, Google Inc.
     * @link      http://www.google.com/recaptcha
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in
     * all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    /**
     * A ReCaptchaResponse is returned from checkAnswer().
     */
    class ReCaptchaResponse
    {
        public $success;
        public $errorCodes;
    }
    class ReCaptcha
    {
        private static $_signupUrl = "https://www.google.com/recaptcha/admin";
        private static $_siteVerifyUrl =
            "https://www.google.com/recaptcha/api/siteverify?";
        private $_secret;
        private static $_version = "php_1.0";
        /**
         * Constructor.
         *
         * @param string $secret shared secret between site and ReCAPTCHA server.
         */
        function ReCaptcha($secret)
        {
            if ($secret == null || $secret == "") {
                die("To use reCAPTCHA you must get an API key from <a href='"
                    . self::$_signupUrl . "'>" . self::$_signupUrl . "</a>");
            }
            $this->_secret=$secret;
        }
        /**
         * Encodes the given data into a query string format.
         *
         * @param array $data array of string elements to be encoded.
         *
         * @return string - encoded request.
         */
        private function _encodeQS($data)
        {
            $req = "";
            foreach ($data as $key => $value) {
                $req .= $key . '=' . urlencode(stripslashes($value)) . '&';
            }
            // Cut the last '&'
            $req=substr($req, 0, strlen($req)-1);
            return $req;
        }
        /**
         * Submits an HTTP GET to a reCAPTCHA server.
         *
         * @param string $path url path to recaptcha server.
         * @param array  $data array of parameters to be sent.
         *
         * @return array response
         */
        private function _submitHTTPGet($path, $data)
        {
            $req = $this->_encodeQS($data);
            $response = file_get_contents($path . $req);
            return $response;
        }
        /**
         * Calls the reCAPTCHA siteverify API to verify whether the user passes
         * CAPTCHA test.
         *
         * @param string $remoteIp   IP address of end user.
         * @param string $response   response string from recaptcha verification.
         *
         * @return ReCaptchaResponse
         */
        public function verifyResponse($remoteIp, $response)
        {
            // Discard empty solution submissions
            if ($response == null || strlen($response) == 0) {
                $recaptchaResponse = new ReCaptchaResponse();
                $recaptchaResponse->success = false;
                $recaptchaResponse->errorCodes = 'missing-input';
                return $recaptchaResponse;
            }
            $getResponse = $this->_submitHttpGet(
                self::$_siteVerifyUrl,
                array (
                    'secret' => $this->_secret,
                    'remoteip' => $remoteIp,
                    'v' => self::$_version,
                    'response' => $response
                )
            );
            $answers = json_decode($getResponse, true);
            $recaptchaResponse = new ReCaptchaResponse();
            if (trim($answers ['success']) == true) {
                $recaptchaResponse->success = true;
            } else {
                $recaptchaResponse->success = false;
                $recaptchaResponse->errorCodes = $answers [error-codes];
            }
            return $recaptchaResponse;
        }
    }
    
    $reCaptcha = new ReCaptcha($secret);
    
    if(isset($_POST["g-recaptcha-response"])) {
        $resp = $reCaptcha->verifyResponse(
            $_SERVER["REMOTE_ADDR"],
            $_POST["g-recaptcha-response"]
            );
        if ($resp != null && $resp->success) {echo "OK";}
        else {echo "CAPTCHA incorrect";}
        }
    ?>
    
    <html>
    
    <head>
    <title>Google reCAPTCHA</title>
    <script src="https://www.google.com/recaptcha/api.js"></script>
    </head>
    
    <body>
    <form action="reCAPTCHA.php" method="POST">
    <input type="submit" value="Submit">
    <div class="g-recaptcha" data-sitekey="<?php echo $siteKey; ?>"></div>
    </form>
    </body>
    
    </html>
    

    通常,它应该工作(只需添加你的私钥和你的公钥),我在我的iPhone SE上测试,2秒前,它工作得很好 .

相关问题