Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google server response changed this morning and causes PHP Client Library Error #359

Open
Domgarth opened this issue Mar 17, 2020 · 15 comments

Comments

@Domgarth
Copy link

Issue description

Between 8.00 and 8.30 CET this morning the response from the google reCAPTCHA servers changed

$recaptcha = new \ReCaptcha\ReCaptcha(MY_KEY, new \ReCaptcha\RequestMethod\SocketPost());

The google servers now return, the json is invalid
see below.

Has anybody else had this problem?

HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Date: Tue, 17 Mar 2020 11:39:47 GMT
Expires: Tue, 17 Mar 2020 11:39:47 GMT
Cache-Control: private, max-age=0
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'
X-XSS-Protection: 1; mode=block
Server: GSE
Alt-Svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,h3-T050=":443"; ma=2592000
Accept-Ranges: none
Vary: Accept-Encoding
Connection: close
Transfer-Encoding: chunked
5a
{
"success": true,
"challenge_ts": "2020-03-17T11:36:43Z",
"hostname": "127.0.0.1"
}
0

This breaks the function public static function fromJson($json) in the lib

Environment
We have seen the problem on 4 systems of different configurations.

@LionelMarbot
Copy link

LionelMarbot commented Mar 17, 2020

We are experiencing the exact same issue since today. We "fixed" this by using CurlPost instead of SocketPost.

@Domgarth
Copy link
Author

Rowan, Is the google server json response valid?
5a
{
"success": true,
"challenge_ts": "2020-03-17T11:36:43Z",
"hostname": "127.0.0.1"
}
0

@pedde07
Copy link

pedde07 commented Mar 19, 2020

Same Problem here.
/ReCaptcha/ReCaptcha.php:96:string '69
{
"success": true,
"challenge_ts": "2020-03-19T11:48:43Z",
"hostname": "dummy.host.name.com"
}
0
' (length=113)

@Domgarth
Copy link
Author

Did you see the fix from Lionel?
Just change SocketPost to CurlPost

@pedde07
Copy link

pedde07 commented Mar 19, 2020

Yes. The fix worked fine.
I just wanted to confirm the problem.

@jonnott
Copy link

jonnott commented Mar 19, 2020

Also fixed by switching to CurlPost instead of SocketPost.

@Domgarth
Copy link
Author

We also fixed the SocketPost by patching the fromJson() in the lib
It's not a proper fix but it works
It may be needed by some as the CurlPost needs a certificate

/**
 * Build the response from the expected JSON returned by the service.
 *
 * @param string $json
 * @return \ReCaptcha\Response
 */
public static function fromJson($json)
{
    $responseData = json_decode($json, true);

    if (!$responseData) {

        $hostname = isset($responseData['hostname']) ? $responseData['hostname'] : null;

        if (strpos($json, 'success') !== false && strpos($json, 'true') !== false) {
            return new Response(true, array(), $hostname);
        }

        return new Response(false, array('invalid-json'));
    }

    $hostname = isset($responseData['hostname']) ? $responseData['hostname'] : null;

    if (isset($responseData['success']) && $responseData['success'] == true) {
        return new Response(true, array(), $hostname);
    }

    if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) {
        return new Response(false, $responseData['error-codes'], $hostname);
    }

    return new Response(false, array(), $hostname);
}

@ntanis-dev
Copy link

The issue is that recently Google servers started to serve chunk-encoded HTTP responses. Until this is fixed in the library, if you can't switch to CurlPost what you need to do is edit SocketPost and decode the response as a potential HTTP chunk-encoded. You can do so either by using a custom function (plenty of examples around the web) or more conveniently if you are using / can use PECL, the pecl-http package includes an http_chunked_decode function. If you end up with a clean solution, feel free to PR it as well. Hope that helps!

@Domgarth
Copy link
Author

Thanks for the explanation.
I wondered why switching to CurlPost fixed the problem.

@ntanis-dev
Copy link

CurlPost does not experience the problem because cURL handles and parses the chunk-encoded HTTP internally, while SocketPost just uses a socket for a raw HTTP request with minimal parsing involved.

@DavidsRaptor
Copy link

We fixed it that way:

/**
 * Build the response from the expected JSON returned by the service.
 *
 * @param string $json
 * @return \ReCaptcha\Response
 */
public static function fromJson($json)
{
    $responseData = false;
    if (self::isJsonValid($json)) {
        $responseData = json_decode($json, true);
    } else {
        $json_pattern = '/\{(?:[^{}]|(?R))*\}/xs';
        preg_match_all($json_pattern, $json, $matches);
        $filtered_json = trim(preg_replace('/\s+/', ' ', $matches[0][0]));
        if (self::isJsonValid($filtered_json)) {
            $responseData = json_decode($filtered_json, true);
        }
    }

    if (!$responseData) {
        return new Response(false, array(ReCaptcha::E_INVALID_JSON));
    }

    $hostname = isset($responseData['hostname']) ? $responseData['hostname'] : null;
    $challengeTs = isset($responseData['challenge_ts']) ? $responseData['challenge_ts'] : null;
    $apkPackageName = isset($responseData['apk_package_name']) ? $responseData['apk_package_name'] : null;
    $score = isset($responseData['score']) ? floatval($responseData['score']) : null;
    $action = isset($responseData['action']) ? $responseData['action'] : null;

    if (isset($responseData['success']) && $responseData['success'] == true) {
        return new Response(true, array(), $hostname, $challengeTs, $apkPackageName, $score, $action);
    }

    if (isset($responseData['error-codes']) && is_array($responseData['error-codes'])) {
        return new Response(false, $responseData['error-codes'], $hostname, $challengeTs, $apkPackageName, $score, $action);
    }

    return new Response(false, array(ReCaptcha::E_UNKNOWN_ERROR), $hostname, $challengeTs, $apkPackageName, $score, $action);
}

/**
 * Checking for JSON validity.
 *
 * @param string $string
 * @return bool
 */
public static function isJsonValid($string) {
    json_decode($string);
    return (json_last_error() == JSON_ERROR_NONE);
}

@gingram-brpc-org
Copy link

We are experiencing the exact same issue since today. We "fixed" this by using CurlPost instead of SocketPost.

Where did you make this switch?

@rowan-m
Copy link
Contributor

rowan-m commented Mar 31, 2020

I've released a super quick fix which is just requesting HTTP 1.0 instead of HTTP 1.1 and as a result - no chunking in the response.

That's a bit dirty though, so I'll update later on to actually handle a chunked response properly.

@rowan-m
Copy link
Contributor

rowan-m commented Mar 31, 2020

1.2.3...1.2.4

juek added a commit to Typesetter/Typesetter that referenced this issue Jun 15, 2020
* fixes recent 'invalid JSON' issues, see
google/recaptcha#359
@Vergil137
Copy link

Vergil137 commented Jun 25, 2020

#383 would fix this issue i think

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

9 participants