preface

Recently have received a letter from foreign security company about safety warning email, roughly means he found our site forgot password, send a validation email didn’t do the function of anti brush, cause he can use a burp suite web security tools such as brush to batch interface, or writing your own scripts to batch brush. In this way, we will send a large number of spam emails, which will not only disturb our users, but also increase the cost of sending emails. In a worse case, our SES account may be closed by AWS, as we have seen before: remember again the situation that AWS SES email service account was banned

To solve

Due to the particularity of this interface (it does not require user login, so there is no so-called cookie, session authentication, let alone AUTHENTICATION such as JWT token), so after excluding authentication, we can only handle it in other ways:

1. Add an anti-brush interface to the gateway

The first thing to consider is to add anti-brush authentication to the interface, such as the nginx gateway to add anti-brush restrictions for the interface, such as the same IP address more than 10 times in a minute, then return 403. At the same time, add this IP address to the blacklist and the ban will be lifted after 24 hours.

2. Add the verification code

In principle, the IP anti-brush mechanism can solve 90% of the problems of flushing interfaces. However, there are some cases of accidental killing. For example, ordinary users may try to enter wrong email addresses for many times, and they may be mistakenly killed because of too many attempts. There is also a more common case of accidental killing. In many cases, the export IP of a certain area (such as school or company) is actually the same. If multiple users use this function together in this area, it will be accidental killing. So a more friendly way is to add a captcha, so when the user submits a request, they have to add a captcha, so the server side will approve the request. In this way, there is no problem with manslaughter, and there is no fear of brush interface. But captchas have the following problems:

  • Captcha experience is not good, after all, the user needs to identify and enter
  • If the captcha mechanism is not strong enough, it is also easy to be broken, especially now that the picture recognition robot and speech recognition robot is actually very good, poor captcha mechanism is easy to be broken, useless

So we ruled out doing it ourselves (which is cheap in terms of both labor and time), looked at mature captcha products on the market, and picked Google reCaptcha.

Google reCaptcha

A lot of websites outside of the country use this service to develop documents: recaptcha Intro

1 Introduction to Google reCaptcha

To keep things simple, Google reCaptcha currently comes in three versions:

reCaptcha v1

Graphic captcha, which requires the user to type the text in the image.

reCaptcha v2

If we choose to use reCaptcha V2, we can create one of the three validation types 1, 2, and 3 for our site. 4 appears when Google thinks you might be a robot. You are required to conduct further verification.

1 checkbox

You need to select the I’m not Robot check box

2 invisible

You do not need to perform any operation. The front end needs to bind related events to the “submit” button (or other event) using the Js API provided by Google. The “I’m not Robot” check box event can be replaced with a custom onSubmit or other event.

Invisible Image Challenge:

In this case, Google will have a mask, which is embedded with an iframe, and will not affect our original UI elements

3 reCAPTCHA Android

ReCaptcha validation in Android devices, which we haven’t looked at yet. If there is an application, make it up

4 Further verification: Image Challenge

V2 reCaptcha requires further validation in several cases (V3 does not) :

  • Too frequent: The same IP address retries this frequently within a certain period of timei'm not a robot(Under V2 Invisible, corresponding events are frequently triggered).
  • recaptcha jsRecaptcha validation is done long after initialization.
  • Check in the browserBlocking third-party cookies
  • Some abnormal mouse or scrollbar behavior, such as no mouse detectedmovementI just clicked on the check box

Reference:

  • ReCaptcha always asking for extra verification?
  • Way to skip reCAPTCHA images challenge
  • google-one-click-recaptcha

reCaptcha v3

No operation is required. The reCaptcha is complete when a score between 0.0 and 1.0 is scored based on the user’s actions and events on the browser, which is handed to the server when the server asks the API to validate it. The server processes subsequent services based on the score obtained and the specific scenario.

V3 does not have an image Challenge.

2 reCaptcha Access Instructions

Access to advice

It is recommended to use v2 because v3 has known and unclosed problems:

  • Github.com/google/reca…
  • Github.com/google/reca…

There was user feedback that all of the scores suddenly changed to 0.1 (including real user testing). This happened to me when I tested myself: I always got 0.9 in Chrome, but I always got 0.1 in Safari.

Browser Support

Almost all modern browsers support it

See support.google.com/recaptcha#6… .

Domestic user access problems

Because reCaptcha access requires front-end loading of JS files under the Google.com domain name, domestic users may not be able to load reCaptcha properly. But Google provides Globally accessible domain names: www.recaptcha.net, and you can use the full functionality of reCapatcha by replacing www.google.com with www.recaptcha.net in the corresponding resource URL.

Reference: reCaptcha FAQ

Security setting issues

There are three options for setting security preferences. It is not known what the differences are between the three options and how these options affect users’ use. At present, no relevant information has been found, and it seems that there is no difference. However, in subsequent tests, when accessing The Google recapTCHA, we found that choosing a level between the most user-friendly and the most secure resulted in image Challenges appearing frequently (almost every time). So we choose the most user-friendly grade.

Known issues

  • You may need to manually set the user in IE to handle the failure to display reCaptcha

3 Development Process

Create with reCaptcha

In www.google.com/recaptcha/a… Page to create a new website. As prompted, we chose the invisible reCaptcha badge from reCaptcha version 2. Tag a random easy to identify, and then fill in the domain name you want to verify the front-end site domain name. After the creation is complete, go to Settings to complete the configuration.

  • Send the “site key” and “key” to the front-end developer.
  • Security preferences, to be selected according to requirements, currently we should choose the “most user-friendly” level.
  • Remember to check “Verify erCAPTCHA solution source”

After the final successful creation, the following output will be generated:

  1. Site Key (for front-end embedded pages)
  2. Secret key (for server-side callsrecaptcha apiValidation)

Therefore, although there is a domain name to be verified, there is no need to verify whether this domain name is owned by you, because there is a complete set of keys to verify. If the keys on the front and back end do not match, authentication fails.

Front end access

After the front end accesses reCaptcha from the document, it submits the request with a G-RECAPtcha-Response parameter. See the reCaptcha access documentation.

<html>
  <head>
    <title>reCAPTCHA demo: Simple page</title>
    <script src="https://www.google.com/recaptcha/api.js" async defer></script>
  </head>
  <body>
    <form action="?" method="POST">
      <div class="g-recaptcha" data-sitekey="your_site_key"></div>
      <br/>
      <input type="submit" value="Submit">
    </form>
  </body>
</html>
Copy the code

Of course, this is the simplest example, most of the time we do not load the HTML page, but another asynchronous loading, such as in real use, our code is as follows:

    <form id="form_resend">
        <div id='recaptcha' class="g-recaptcha" data-sitekey="" data-callback="grecaptchaCallback"
            data-size="invisible"></div>
        <label for="email">Please enter your email address and we will send you an email to reset your password</label>
        <input id="email" type="email" name="mail" placeholder="Email" autocomplete="off" />
        <button id="submit" class="button-primary" name="submit" value="submit"
            type="submit">Send E-mail</button>
    </form>
Copy the code

When initializing, load it in js code:

$('#recaptcha').attr('data-sitekey'."xxxxxxxxxxxxxxxxxxxx")
window.grecaptchaCallback = function(token){
    // Send to the server
    self.server.sendResetpwMail({
        mail: $("#mail").val().trim(),
        g_token:token
    }).done(function (resp) {
        // success
    }).fail(function () {
        // fail
    }).always(function () {
        // Reset the state
        grecaptcha.reset();
        // do something tip
    });
};
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://www.recaptcha.net/recaptcha/api.js";
script.async = 'async';
script.defer = 'defer';
document.body.appendChild(script);
Copy the code

That’s the logic. The next step is to bind the Form’s Submit event and fire Grecaptcha.execute (), which, if successful, fires the global object grecaptchaCallback and passes the token to the front end. Then the front-end, along with the token and key, passes to the server for verification.

grecaptchaExecute:function(e){
    e.preventDefault();
    if (!this.validEmail()) return false;
    grecaptcha.reset();
    grecaptcha.execute();
}
Copy the code

Server-side authentication

The server accepts the G-RECAPtcha-Response parameter in the front end request and then invokes the recapTCHA validation interface to determine whether the validation has passed based on the value returned by the validation interface.

If you don’t check verification of reCaptcha solution source when you create reCaptcha, anyone can take a site key from your HTML form or JS on your site, embed it in their own site (hack.com) and submit it themselves, And get the correct G-recapTcha-response. Then use the response token to go to your interface for a forged submission. If the server calls the reCaptcha validation interface at this point, the result is passed. The solution is that the server must verify not only the “pass” but also the “source”.

If you check verify reCaptcha solution source when you create reCaptcha, an attacker can still get our site key and get the correct G-TCHa-Response and submit it to our server. However, when the reCaptcha interface is called, the server determines that the reCaptcha fails and returns incorrect captcha-sol.

See Server Validation.

The specific code is:

            // Google recaptCHA token validation is performed when a request comes from the official website. If the verification fails, the request is terminated.
            $gtoken = Request::getParam('g_token');
            $isGTokenValid = Request::checkGoogleRecaptcha($gtoken);
            if (! $isGTokenValid) {
                LogService::logWarning("mail : $mail, gtoken invalid : $gtoken");
                Yii::$app->end();
            }
Copy the code

Then verify the token:


/** * Send a POST request and return the result *@param $url
 * @paramArray $formData form format *@param int $timeout
 * @return bool|string
 */
public static function httpPost($url, $formData = [], $timeout = 5)
{
    LogService::logInfo("Guzzle Request Post Url : $url");
    $client = new Client(['timeout' => $timeout]);

    try {
        $response = $client->request('POST', $url, [
            'form_params' => $formData,
        ]);
    } catch (\Exception $e) {
        LogService::logError("Guzzle Exception : $url , " . $e->getMessage());
       return false;
    }

    return $response->getBody()->getContents();
}

 /** * Validates the Google recapTCHA token (this method currently only supports V2 recaptCHA) *@param $token
 * @return bool
 */
public static function checkGoogleRecaptcha($token = ' ')
{
    $formData = [
        'secret' => yiicfg('googleRecaptchaSecretKey'),
        'response' => $token,
    ];

    $resp = Request::httpPost(yiicfg('googleRecaptchaApiUrl'), $formData);

    // If the RECAPTCHA API is an exception, the recaptCHA passes
    if ($resp === false || is_string($resp) === false) {
        return true;
    }

    $jsonData = json_decode($resp, true);
    if (isset($jsonData['success']) && $jsonData['success'] = =false ) {
        $errcode = isset($jsonData['error-codes'])? $jsonData['error-codes'] : [];
        LogService::logWarning("gtoken invalid : $token, error : " . json_encode($errcode));
        return false;
    }

    return true;
}
Copy the code

This completes the validation on the server. The verification error code returned by Google looks like this:

conclusion

In fact, for most normal users, there is basically no mandatory image verification code, so the user experience is almost the same as before, only users who swipe the interface or frequently operate the page, Google will decide whether to display the image verification code.


For more articles, please go to kebingzao.com