Help owasp -valid url is being rejected

1. The problem I’m having:

owasp is rejecting valid requests : uri “/?v-r=heartbeat&v-uiId=0”

I’ve tried chat gpt but it keeps going around in circles.

2. Error messages and/or full log output:

{"level":"error","ts":1759296096.8112571,"logger":"http.handlers.waf","msg":"[client \"223.123.105.210\"] Coraza: Warning. Inbound Anomaly Score Exceeded (Total Score: 5) [file \"@owasp_crs/REQUEST-949-BLOCKING-EVALUATION.conf\"] [line \"7441\"] [id \"949110\"] [rev \"\"] [msg \"Inbound Anomaly Score Exceeded (Total Score: 5)\"] [data \"\"] [severity \"emergency\"] [ver \"OWASP_CRS/4.15.0\"] [maturity \"0\"] [accuracy \"0\"] [tag \"anomaly-evaluation\"] [tag \"OWASP_CRS\"] [hostname \"\"] [uri \"/?v-r=heartbeat&v-uiId=0\"] [unique_id \"LCpuCdULGzCkuPnA\"]"}
PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.

3. Caddy version:

caddy --version
v2.10.0 h1:fonubSaQKF1YANl8TXqGcn4IbIRUDdfAkpcsfI/vX5U=

4. How I installed and ran Caddy:

I’m running caddy in a docker container:


 # we build our own caddy file as we need the cloudflare module.
FROM caddy:2.10.0-builder AS builder


# https://caddyserver.com/docs/modules/dns.providers.cloudflare
RUN xcaddy build \
    --with github.com/caddy-dns/cloudflare \
    --with github.com/WeidiDeng/caddy-cloudflare-ip \
    --with github.com/corazawaf/coraza-caddy/v2


FROM caddy:2.10.0

COPY --from=builder /usr/bin/caddy /usr/bin/caddy

# Set custom directories for data and config
ENV XDG_CONFIG_HOME=/opt/onepub/caddy/config
ENV XDG_DATA_HOME=/opt/onepub/caddy/data

# RUN mkdir -p /caddy
# coraza WAF configuation directory
RUN mkdir -p /opt/onepub/caddy/coraza

COPY config/caddy/ /opt/onepub/caddy/
# waf configuration files.
COPY config/coraza /opt/onepub/caddy/coraza
# so we know where to put any custom waf commands.
RUN touch /opt/onepub/caddy/coraza/waf-directives.conf

CMD ["caddy", "run", "--config", "/opt/onepub/caddy/Caddyfile", "--adapter", "caddyfile"]


   

a. System environment:

docker

b. Command:

PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.

n/a

c. Service/unit/compose file:

PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.

  caddy:
    container_name: caddy
    image: onepub/onepub-caddy:${ONEPUB_VERSION}
    restart: always
    network_mode: "host"
    cap_add:
      - NET_ADMIN
    environment:
      ACME_AGREE: "true"
      ACME_URL: ${ACME_URL} # staging or production
      EMAIL: ${AUTH_PROVIDER_EMAIL_ADDRESS}
      CLOUDFLARE_API_TOKEN: ${CLOUDFLARE_API_TOKEN}
      ACTIVE_SITE: ${ACTIVE_SITE}

    ports:
      - 80:80
      - 443:443
      - 443:443/udp

    volumes:
      # Persist certificates
      - caddy:/caddy
      - filestore:/opt/onepub/filestore
      #- /opt/onepub/caddy:/opt/onepub/caddy

      # Logs
      - /tmp/caddy:/var/log/caddy

    logging:
      driver: "journald"

d. My complete Caddy config:

PASTE OVER THIS, BETWEEN THE ``` LINES.
Please use the preview pane to ensure it looks nice.

5. Links to relevant resources:

My caddy config:

{
        # ===== Global =====
        email {$EMAIL}
        acme_ca {$ACME_URL:"https://acme-staging-v02.api.letsencrypt.org/directory"}

        servers {
                trusted_proxies cloudflare {
                        interval 12h
                        timeout 15s
                }
        }

        order coraza_waf first
}


import common.caddy

# Only these will be loaded; set ACTIVE_IMPORTS to a space-separated list of site files.
# e.g.
#   ACTIVE_SITE="sites/production/*.caddy"
#   ACTIVE_SITE="sites/beta/*.caddy"
#   ACTIVE_SITE="sites/dev/*.caddy"
import "{$ACTIVE_SITE}"


The 'ACTIVE_SITE' import


# Primary: onepub.dev + wildcard (prod & beta share app routes)
# The wild card is required to support www.onepub.dev
onepub.dev, *.onepub.dev {
        import cloudflare-tls
        import common-security
        import common-app-routes

        # Beta-specific robots
        @beta host beta.onepub.dev
        header @beta {
                X-Robots-Tag "noindex, nofollow"
        }

        # MailHog ONLY for beta.onepub.dev
        handle @beta {
                import common-mailhog
        }
}
common.caddy
   


# ===== Reusable TLS (single CF token for all zones) =====
(cloudflare-tls) {
        tls {
                dns cloudflare {
                        api_token {$CLOUDFLARE_API_TOKEN}
                }
        }
}

# ===== Reusable snippets =====
(common-security) {
        header {
                Strict-Transport-Security "max-age=31536000; includeSubDomains"
                X-Frame-Options "SAMEORIGIN"
                X-Content-Type-Options "nosniff"
        }
        @block_ai header_regexp User-Agent "(?i)(GPTBot|ChatGPT|Google-Extended|Claude-Web|Anthropic|Amazonbot|FacebookBot|cohere-ai|Bytespider|YouBot)"
        respond @block_ai 403
        @block_php path *.php
        abort @block_php
        @multiple_slashes path_regexp multipleSlashes ^(.*)//+(.*)$
        redir @multiple_slashes {scheme}://{host}{re.multipleSlashes.1}/{re.multipleSlashes.2}{query} permanent
        encode gzip
}

(common-waf) {
        coraza_waf {
                load_owasp_crs
                directives `
                Include @coraza.conf-recommended
                Include @crs-setup.conf.example
                Include @owasp_crs/*.conf
                Include /opt/onepub/caddy/coraza/waf-directives.conf
                `
        }
}

(common-proxy8080) {
        reverse_proxy 127.0.0.1:8080 {
                header_up Host {host}
                header_up X-Real-IP {remote_host}
                transport http {
                        read_timeout 300s
                }
                header_up Connection {header.Connection}
                header_up Upgrade {header.Upgrade}
        }
}

(common-app-routes) {

        # we need to bypass WAF for some of our internal
        # end points that look a litte odd to WAF.

        # WebSocket upgrades (keep first)
        @ws {
                header_regexp Upgrade (?i)websocket
                header_regexp Connection (?i)\bupgrade\b
        }
        handle @ws { 
            import common-proxy8080 
        }

        # UIDL calls like "/?v-r=uidl&v-uiId=..."
        @vaadin_uidl {
                path /
                query v-r=uidl
                query v-uiId=*
        }
        handle @vaadin_uidl { 
                import common-proxy8080 
        }

        # Vaadin Push long-poll/stream: "/VAADIN/push?v-r=push&v-uiId=...&v-pushId=..."
        @vaadin_push {
                path /VAADIN/push
                query v-r=push
                query v-uiId=*
                query v-pushId=*
        }
        handle @vaadin_push { 
                import common-proxy8080 
        }

        # Your endpoint that currently sends text/plain
        @visibility path /api/visibility
        handle @visibility { 
                import common-proxy8080 
        }

        # Heartbeat (already bypassed, keep consistent)
        @heartbeat path /api/clientheartbeat
        handle @heartbeat { 
                import common-proxy8080 
        }


        # Heartbeat (root path, query v-r=heartbeat)
        @vaadin_heartbeat {
                path /
                query v-r=heartbeat
                query v-uiId=*
        }
        
        handle @vaadin_heartbeat {
                import common-proxy8080
        }


        # Everything else under WAF
        handle {
                import common-waf
                import common-proxy8080
        }

        # API error mapping
        handle_errors {
                @api path /api/*
                handle @api {
                        root * /etc/caddy/json
                        rewrite 502 /500.json
                        rewrite 404 /404.json
                        file_server
                }
        }

        # Default upstream
        import common-proxy8080
}

(common-mailhog) {
        handle /mailhog/* {
                basic_auth {
                        {$EMAIL} XXXXXXXXXXXXXXXXXXXXXXxxx
                }
                reverse_proxy 127.0.0.1:8025
        }
}


(common-video) {
  # TLS via Cloudflare DNS for video hosts
  import cloudflare-tls

  # Security headers, gzip, etc.
  import common-security

  # Apply WAF by default (your app is serving the video files)
  import common-waf

  # MP4 proxy (shared)
  @mp4 path *.mp4
  handle @mp4 {
    reverse_proxy 127.0.0.1:8080 {
      header_up Host {host}
      header_up X-Real-IP {remote_host}
      transport http {
        read_timeout 300s
      }
      header_up Connection {header.Connection}
      header_up Upgrade {header.Upgrade}
    }
  }
}


This looks like a Coraza-specific issue. You might have better luck asking about it in the Coraza community.

2 Likes

thanks, I will give them a try.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.