Browse Source

Fix logic for recaptcha v3 on error states (#698)

Certain error states in the recaptcha v3 API do not return a score.
However, we were defaulting to a "good" score and therefore the errors
were being let through.

This first checks if the request was successful. If so, it then compares
the score.

Fallbacks have also been changed from "good" states to sentinel values,
which should be helpful for debugging in production.
Vince Salvino 1 day ago
parent
commit
9b0fc7017c
2 changed files with 11 additions and 7 deletions
  1. 4 1
      coderedcms/models/page_models.py
  2. 7 6
      coderedcms/recaptcha.py

+ 4 - 1
coderedcms/models/page_models.py

@@ -1640,7 +1640,10 @@ class CoderedFormMixin(models.Model):
                 utils.get_ip(request),
             )
             # Score ranges from 0 (likely spam) to 1 (likely good).
-            return rr.score < ls.recaptcha_threshold
+            if rr.success and rr.score >= 0:
+                return rr.score < ls.recaptcha_threshold
+            else:
+                return True
         elif ls.spam_service == ls.SpamService.RECAPTCHA_V2:
             rr = verify_response(
                 request.POST.get("g-recaptcha-response", ""),

+ 7 - 6
coderedcms/recaptcha.py

@@ -10,7 +10,7 @@ logger = logging.getLogger("coderedcms")
 
 
 class RecaptchaResponse(typing.NamedTuple):
-    success: bool
+    success: typing.Union[bool, None]
     score: float
     error_codes: typing.List[str]
     original_data: typing.Dict[str, typing.Any]
@@ -43,11 +43,12 @@ def verify_response(recaptcha_response: str, secret_key: str, remoteip: str):
     response = urlopen(request)
     data = json.loads(response.read().decode("utf8"))
     response.close()
-    logger.info(f"reCAPTCHA response: {data}")
-    # Default to good (likely not spam) values if they are not present.
-    return RecaptchaResponse(
-        success=data.get("success", True),
-        score=data.get("score", 1.0),
+    # Default to sentinel values if not provided by Google.
+    rr = RecaptchaResponse(
+        success=data.get("success", None),
+        score=data.get("score", -1.0),
         error_codes=data.get("error-codes", []),
         original_data=data,
     )
+    logger.info(f"reCAPTCHA response: {rr}")
+    return rr