ReCaptcha在iPhone上无法正常工作

我有一个简单的联系表格的网站。 validation是有点微不足道的,因为它不会进入数据库; 只是电子邮件。 表格是这样工作的:

有5个领域 – 其中4个是必需的。 提交被禁用,直到4个字段有效,然后您可以提交。 然后再次在服务器上validation一切,包括recaptcha(这不是我客户端validation的)。 整个过程使用ajax完成,并且有多个testing必须在服务器端传递,或返回4 **头,并调用失败callback处理程序。

一切都像在桌面上的Chrome浏览器(我还没有尝试其他浏览器,但我无法想象为什么他们会有所不同),但在iPhone上reCaptcha总是validation,即使我不勾选框考试。

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

如果有人认为这会有帮助,我可以发布一些代码,但问题出在设备上而不是代码上。 有没有人有任何见解?


注意 :如果这有帮助的话,服务器端是PHP / Apache。


更新date:5/28/2015

我仍然在debugging这个,但它似乎像移动Safari浏览器忽略我的iPhone响应头。 当我输出对页面的响应,我得到的桌面上(data,status,xhr)是:

  1. data我的回应,在这一点上只是说错误或成功 – > error

  2. statuserror

  3. xhr{'error',400,'error'}

在移动Safari浏览器:

  1. dataerror

  2. statussuccess

  3. xhr{'error',200,'success'}

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


更新:6/3/2015

每个请求,这里是代码。 这几乎肯定比你需要的多。 它也变得更加钝了,因为我已经做了修改和修改。 还要注意的是,虽然看起来有些variables还没有被定义,但是(应该)已经在其他文件中定义了。

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; } } 

像这样的问题iOS:使用XMLHttpRequest进行身份validation – 处理401响应最简单的方法来解决这个问题是无视自然头validation,并在callback成功,用一些标志进行validation。

我已经看到了一些这样的情况,从来没有闻到好。

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

即使你的Ajax请求发送了一个空或者是假的值,你的php脚本中的isset()函数也会返回true,从而错误地填充$ remoteIp。

尝试做:

 $remoteIp = $_SERVER['REMOTE_ADDR']; 

Ajax只是使浏览器做请求,因此PHP可以完美地抓住我们的用户的IP。

我敢肯定,如果你传递了错误的价值,ReCaptcha会以某种方式陷入困境。

永远不要信任Ajax上的任何Javascriptvariables也更安全,因为这些也应该作为用户input。

validation码旨在防止恶意客户端(机器人) ,所以理论上如果客户端绕过validation码,这是一个服务器端问题。 (但是,如果客户端未能完成validation码,则可能是服务器端问题或客户端问题。)

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

我在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> 

通常情况下,它应该工作(只需添加您的私钥和公钥),我在2秒钟之前在我的iPhone SE上进行testing,并且工作完美。