ReCaptcha在iPhone

时间:2015-05-27 13:18:55

标签: php iphone validation recaptcha

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

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

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

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

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

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

更新:2015年5月28日

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

  1. data我的回答,此时只是说错误或成功 - > error

  2. statuserror

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

  4. On Mobile safari:

    1. dataerror

    2. statussuccess

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

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

      更新:2015年6月3日

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

      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 个答案:

答案 0 :(得分:1)

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

我看过一些像这样的案子,从来没有闻到过。

答案 1 :(得分:0)

你的" remoteIP"变量在客户端正确设置?

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

尝试做:

$remoteIp = $_SERVER['REMOTE_ADDR'];

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

我确信,如果你传递错误的价值,ReCaptcha会以这种或那种方式陷入困境。

从不相信Ajax上的任何Javascript变量也更安全,因为这些变量也应被视为用户输入。

答案 2 :(得分:0)

验证码旨在防止恶意客户端(机器人),因此理论上如果客户端绕过验证码,则是服务器端问题。(但是,如果是客户端无法完成验证码,可能是服务器端问题或客户端问题。)

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

答案 3 :(得分: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>

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