首页 > 解决方案 > 如何在服务器端验证 google reCaptcha?

问题描述

我实施了 reCaptcha。单击“我不是机器人”复选框后,将从谷歌生成一个令牌。

客户端(js)

    function checkCaptchaAndSubscribe(thisContext)
    {
        var captchaResponse = grecaptcha.getResponse();

        if (captchaResponse == "") {
            $captchaRequired.css('display', 'block');
            return false;
        }

        grecaptcha.reset();
        $captchaRequired.css('display', 'none');

        jQuery.ajax({
            url: "/black_newsletter2go/index/verify",
            method: "POST",
            async: "true",
            data: {
                recaptchaResponse: captchaResponse
            },
            success: function(response) {

                $statusContainer.show();

                if (response != "success") {
                    $status.html("<h2 class='nl2go_h2'>Die Captcha Validierung ist fehlgeschlagen!</h2>");
                    return false;
                }

                subscribe(thisContext);
            }
        });
    }

我使用 ajax 将令牌发送到我的服务器并在那里验证它,如下所示:

服务器端(php):

    public function verifyAction()
    {
        $captchaResponse = $this->getRequest()->getParam('recaptchaResponse');

        if (!isset($captchaResponse) || empty($captchaResponse)) {
            return "captcha response is empty";
        }

        $secretKey = Mage::Helper("recaptcha")->getSecretKey();

        $url = 'https://www.google.com/recaptcha/api/siteverify';
        $data = array(
            'secret' => $secretKey,
            'response' => $captchaResponse,
        );

        // use key 'http' even if you send the request to https://...
        $options = array(
            'http' => array(
                'header'  => "Content-type: application/x-www-form-urlencoded\r\n",
                'method'  => 'POST',
                'content' => http_build_query($data)
            )
        );

        $context  = stream_context_create($options);

        $result = file_get_contents($url, false, $context);
        $result = json_decode($result);

        //var_dump($result);
        //exit();

        if ($result->success
                && (strpos(Mage::getBaseUrl(), $result->hostname) !== false)) {
            echo "success";
        } else {
            echo "fail";
        }
    }

这是对象 $result 的输出

在此处输入图像描述

success如果检查成功则返回,否则返回fail

但这足够了吗?如果攻击者使用 burpsuite 之类的 HTTP 代理将响应更改为success? 那么他就可以绕过我的检查并且总是通过吗?还是我错了?

标签: javascriptphpmagento-1.9recaptcha

解决方案


它使用密钥对来加密/解密信息。所以它正在发送加密的信息。这就是它不能被篡改的原因,但是当然,这意味着你必须确保私钥不会被盗。

服务器知道状态并将其保存在其存储中,因此如果客户端在“失败”时尝试将其用作“成功”,则无论如何服务器都会知道。所以对于黑客来说,为客户端改变值的可能性不大,当然这取决于你的代码。如果您使用该 reCAPTCHA 来登录用户,那么如果 reCAPTCHA 返回“失败”,那么显然该登录尝试将在服务器端失败。因此,无论客户端是否被告知“成功”,它仍然不会登录。客户端永远不应该成为这种状态的保持者,因为它不能被信任(它总是可能有被污染的数据。)

它的工作方式类似于您在浏览器和使用 HTTPS 的服务器之间所做的工作。

您的客户端和服务器之间的通信也应该在 HTTPS 上,以避免一些更容易的中间人 (MITM) 问题。但是,总是有可能有人成为代理,这是大多数 MITM 的工作方式,在这种情况下,MITM 可以更改您所做的任何事情。

然而,MITM 不能做的一件事是为最终目的地创建有效证书。从这个意义上说,有一种保护,但许多人不会在每次连接到网站时验证证书。但是,一种技术是 MITM 不给你 HTTPS,只有他和你的服务器会使用 HTTPS,而客户端会保持在 HTTP 上。尽管您的代码可以检测到这种情况,但显然 MITM 也可以更改该代码。同样,设置 cookie可以增强安全性Http-OnlySecure但也可以被 MITM 拦截。

由于 MITM 可以完全更改您的脚本,因此您在客户端几乎无法做任何事情来帮助检测此类问题,而在服务器端,您将收到类似于客户端发送给您的命中。再说一次,没有真正的方法来检测 MITM。

有一篇帖子提出了这个问题:我可以从服务器端检测到 MITM 吗?这并非不可能,但相当棘手。通过对普通 HTTP 解决方案的新实现/扩展来实施一些解决方案,但这些解决方案需要一个连接到不同系统的附加应用程序,而且一旦有足够多的人使用这些解决方案,没有理由不能由 MITM 代理解决方案。


推荐阅读