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)
是:
-
data
: 我的回应,在这一点上只是说错误或成功 – >error
-
status
:error
-
xhr
:{'error',400,'error'}
在移动Safari浏览器:
-
data
:error
-
status
:success
-
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,并且工作完美。