1

This is follow up question to another one about reCaptcha and GCA.

I have GKE applications in following setup:

  • front app works on example.com
  • backend app works on api.example.com

I expose those loads via Ingress and all looks cool. I want to protect application with Cloud Armor. I added annotation to api service. I can confirm that if policy has just one rule "deny all IPs" I cannot reach backend endpoints and if I change rule to "allow all IPs" I can. So GCA itself works ok.

I tried to connect reCaptcha Enterprise and interpret its score with Google Cloud Armor but I cannot make it work. I created following rules but whatever values I add token.recaptcha.score doesn't seem to be interpreted at all.

I have 3 following rules:

  • priority: 500, expression: "request.method == 'OPTIONS'"
  • priority: 1000, expression: "token.recaptcha.score > 0.1"
  • priority: 2147483647, expression: deny(403) from *.

I can see that OPTIONS passes:

{
  "insertId": "1xxxr",
  "jsonPayload": {
    "@type": "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry",
    "enforcedSecurityPolicy": {
      "priority": 500,
      "configuredAction": "ALLOW",
      "outcome": "ACCEPT",
      "name": "login-security-policy"
    },
    "statusDetails": "response_sent_by_backend"
  },
  "httpRequest": {
    "requestMethod": "OPTIONS",
    "requestUrl": "https://api.example.com/v1/graphs?pageSize=10&orderBy=created_at%20desc&key=AxxxE",
    "requestSize": "97",
    "status": 200,
    "responseSize": "367",
    "userAgent": "XXX",
    "remoteIp": "XX.XX.XX.XX",
    "referer": "https://example.com/",
    "serverIp": "10.10.0.33",
    "latency": "0.126041s"
  },
  "resource": {
    "type": "http_load_balancer",
    "labels": {
      "project_id": "xxx",
      "zone": "global",
      "url_map_name": "k8s2-um-dxxxd-default-main-ixxxq",
      "backend_service_name": "k8s-be-3xxx9--9xxx9",
      "target_proxy_name": "k8s2-ts-dxxxd-default-main-ixxxq",
      "forwarding_rule_name": "k8s2-fs-dxxxd-default-main-ixxxq"
    }
  },
  "timestamp": "2021-12-21T18:28:42.103879Z",
  "severity": "INFO",
  "logName": "projects/xxx/logs/requests",
  "trace": "projects/xxx/traces/3xxx2",
  "receiveTimestamp": "2021-12-21T18:28:43.228371341Z",
  "spanId": "5xxx0"
}

but then GET doesn't:

{
  "insertId": "1xxxr",
  "jsonPayload": {
    "statusDetails": "denied_by_security_policy",
    "enforcedSecurityPolicy": {
      "outcome": "DENY",
      "configuredAction": "DENY",
      "name": "login-security-policy",
      "priority": 2147483647
    },
    "@type": "type.googleapis.com/google.cloud.loadbalancing.type.LoadBalancerLogEntry"
  },
  "httpRequest": {
    "requestMethod": "GET",
    "requestUrl": "https://api.example.com/v1/graphs?pageSize=10&orderBy=created_at%20desc&key=AxxxE",
    "requestSize": "1291",
    "status": 403,
    "responseSize": "194",
    "userAgent": "XXX",
    "remoteIp": "XX.XX.XX.XX",
    "referer": "https://example.com/",
    "latency": "0.221476s"
  },
  "resource": {
    "type": "http_load_balancer",
    "labels": {
      "project_id": "xxx",
      "target_proxy_name": "k8s2-ts-dxxxd-default-main-ixxxq",
      "forwarding_rule_name": "k8s2-fs-dxxxd-default-main-ixxxq",
      "zone": "global",
      "url_map_name": "k8s2-um-dxxxd-default-main-ixxxq",
      "backend_service_name": "k8s-be-3xxx9--9xxx9"
    }
  },
  "timestamp": "2021-12-21T18:28:42.254613Z",
  "severity": "WARNING",
  "logName": "projects/xxx/logs/requests",
  "trace": "projects/xxx/traces/5xxx0",
  "receiveTimestamp": "2021-12-21T18:28:43.228371341Z",
  "spanId": "axxxb"
}

Here is also this GET request exported from browser:

curl 'https://api.example.com/v1/graphs?pageSize=10&orderBy=created_at%20desc&key=AxxxE' \
  -H 'authority: api.example.com' \
  -H 'pragma: no-cache' \
  -H 'cache-control: no-cache' \
  -H 'accept: application/json, text/plain, */*' \
  -H 'authorization: Bearer exxxw' \
  -H 'x-recaptcha-token: Axxxo' \
  -H 'user-agent: XXX' \
  -H 'origin: https://example.com' \
  -H 'sec-fetch-site: same-site' \
  -H 'sec-fetch-mode: cors' \
  -H 'sec-fetch-dest: empty' \
  -H 'referer: https://example.com/' \
  --compressed
Mateusz
  • 1,149
  • 1
  • 16
  • 33
  • I am not a reCaptcha expert. Edit your question with more context. I am assuming you are using reCaptcha Enterprise. 1) Typically reCaptcha information is sent to the server as a POST. What is your design? 2) You cannot replay reCaptchas. That would defeat the purpose and allow hackers to bypass. In that case, the ALLOW should fail so your rule is working. 3) There is a also time limit of 120 seconds. 4) For testing, I would create a DENY rule (token.recaptcha.score < 0.N) before the ALLOW so that you can see requests that fail based on the rule's criteria. – John Hanley Dec 21 '21 at 19:14
  • I like to use the rule **--action** and return a 302 and a different URL for each case I am testing. This helps me with debugging. **--action redirect --redirect-type external-302 --redirect-target REDIRECT_URL** – John Hanley Dec 21 '21 at 19:28
  • FYI I still fight with this issue. As far as I can tell, when reCAPTCHA key gets whitelisted by Google team (as described in https://cloud.google.com/recaptcha-enterprise/docs/implement-tokens#action-token) it starts to produce malformed tokens. I implemented assessment in backend application just to check what exactly is being sent to backend and I can see that if reCAPTCHA key is not whitelisted for Cloud Armor it produces correct tokens, but after whitelisting it it stops. – Mateusz Dec 30 '21 at 00:10
  • 2
    Open a support case with Google. I am interested in the outcome. – John Hanley Dec 30 '21 at 00:22
  • Finally it appeared that there was error in GCP. It all works now. – Mateusz Mar 23 '22 at 18:50
  • 1
    Do you know what the error was in the end? I am facing the same issue. Tried everything, their support is not very helpful. – Andrei Balici Aug 24 '22 at 07:06
  • For me I think the problem is with the domain verification. If I disable the domain verification from the recaptcha key, the cloud armor rules are applied, but with the domain verification it always returns 403. Even though I add a lot of domains to test. Someone knows why? – Alejandro Barone May 11 '23 at 01:08
  • What do you mean by `reCAPTCHA key is not whitelisted for Cloud Armor`? , where did you see this?, I saw de link you pass but they do not metion anything about whitelisting keys @Mateusz am strugling right now in why my tokens are not working – Alejandro Barone May 11 '23 at 19:11

1 Answers1

0

reCAPTCHA WAF is a new feature reCAPTCHA enterprise have. It put reCAPTCHA at edge layer, protecting web application in WAF(Web application Firewall), before hitting the application server.

recaptcha waf token is different from the normal recaptcha enterprise token.

Make sure reCAPTCHA waf token is visible to Cloud Armor. If you are using the session-token case, it will be the cookie, should automatically visible to Cloud Armor. If you are using the action-token case, make sure the token is attached at the request header, see the integration guide for action-token.

Jessy
  • 1