Skip to content

CKAN API blocked by WAF unless browser headers used #7

Description

@aborruso

Some CKAN portals (example: https://dati.anticorruzione.it/opendata) reject MCP requests with a WAF “Request Rejected” HTML response. The current default headers (custom UA) are too minimal, so the WAF blocks the request before it reaches CKAN.
Proposed fix (agreed):

  • Send browser-like headers by default (Accept, Accept-Language, Accept-Encoding, Sec-* headers, Referer).
  • Use a Chrome-like User-Agent string globally.
    Local test with these headers returns JSON successfully (count=69) for the example portal.
    Notes:
  • This is a general fix (not per-portal) to maximize compatibility.

Thank you very much @nelsonmau


Discussed in #6

Originally posted by nelsonmau January 28, 2026

Making-of: estrazione appalti "Intelligenza Artificiale" da ANAC

Obiettivo

Estrarre dal portale CKAN di ANAC (https://dati.anticorruzione.it) tutti gli appalti degli anni 2023, 2024 e 2025 che contengono la stringa "intelligenza artificiale", esportandoli in CSV con tutti i 61 campi originali.

Portale target


Fase 1: tentativo con Borruso (MCP CKAN tools)

Cosa ho provato

Ho tentato di usare i tool MCP Borruso per interrogare il portale ANAC. Tutti i tentativi sono falliti con la stessa risposta: il WAF del server ha bloccato le richieste restituendo una pagina HTML "Request Rejected".

Tool testati

  1. ckan_package_search - per cercare i dataset
  2. ckan_status_show - per verificare la disponibilità del server
  3. ckan_datastore_search - per interrogare i dati tabellari (non testato, il DataStore risulta non attivo su questo portale)

URL provati come server_url

https://dati.anticorruzione.it
https://dati.anticorruzione.it/opendata
https://dati.anticorruzione.it/opendata/dataset
https://dati.anticorruzione.it/opendata/api/3

Risposta del WAF (identica per ogni tentativo)

<html><head><title>Request Rejected</title></head>
<body>The requested URL was rejected. Please consult with your administrator.<br/><br/>
Your support ID is XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX<br/><br/>
<a href='javascript:history.back();'>[Go Back]</a></body></html>

Diagnosi

Il WAF di ANAC filtra le richieste in base all'User-Agent (e probabilmente anche ad altri header HTTP). Le richieste inviate dai tool MCP Borruso vengono identificate come automatiche e bloccate. Lo stesso comportamento si verifica con curl senza User-Agent personalizzato:

# Bloccato dal WAF
curl -s "https://dati.anticorruzione.it/opendata/api/3/action/package_list"
# -> HTML "Request Rejected"

# Funziona con User-Agent browser
curl -s -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" \
  "https://dati.anticorruzione.it/opendata/api/3/action/package_list"
# -> JSON valido con la lista dei dataset

Nota aggiuntiva: API GET vs POST

Il portale ANAC blocca le richieste GET con query string (?id=cig-2025) anche con User-Agent corretto. Funziona invece il POST con body JSON:

# GET con parametri: BLOCCATO dal WAF
curl -s -A "Mozilla/5.0 ..." \
  "https://dati.anticorruzione.it/opendata/api/3/action/package_show?id=cig-2025"

# POST con JSON body: FUNZIONA
curl -s -A "Mozilla/5.0 ..." \
  -H "Content-Type: application/json" \
  "https://dati.anticorruzione.it/opendata/api/3/action/package_show" \
  -d '{"id":"cig-2025"}'

L'unica eccezione e' package_list che funziona in GET (probabilmente perche' non ha parametri nella query string).

Altra nota: DataStore non disponibile

curl -s -A "Mozilla/5.0 ..." \
  -H "Content-Type: application/json" \
  "https://dati.anticorruzione.it/opendata/api/3/action/datastore_search" \
  -d '{"resource_id":"...","limit":1}'
# -> "Richiesta non valida - Nome di azione non nota: datastore_search"

Il DataStore non e' abilitato su questo portale. Quindi anche se Borruso avesse raggiunto il server, ckan_datastore_search e ckan_datastore_search_sql non sarebbero stati utilizzabili.


Fase 2: workaround con curl + bash

Dato che Borruso non poteva raggiungere il server, ho completato l'estrazione via bash.

Step 1: lista dataset

curl -s -A "Mozilla/5.0 ..." \
  "https://dati.anticorruzione.it/opendata/api/3/action/package_list"

Risultato: 68 dataset, tra cui cig-2023, cig-2024, cig-2025.

Step 2: metadati dataset

curl -s -A "Mozilla/5.0 ..." \
  -H "Content-Type: application/json" \
  "https://dati.anticorruzione.it/opendata/api/3/action/package_show" \
  -d '{"id":"cig-2025"}'

Ogni dataset CIG annuale contiene 37 risorse: 12 CSV + 12 JSON + 12 TTL + 1 log, suddivise per mese (01-12).

Step 3: download dei file CSV

I file CSV sono ZIP disponibili all'URL:

https://dati.anticorruzione.it/opendata/download/dataset/cig-{anno}/filesystem/cig_csv_{anno}_{mese}.zip

Il download funziona con User-Agent browser. Per ogni anno ho scaricato i 12 file mensili in parallelo.

Dimensioni scaricate:

  • 2023: ~117 MB (ZIP) -> ~530 MB (CSV)
  • 2024: ~210 MB (ZIP) -> ~950 MB (CSV)
  • 2025: ~270 MB (ZIP) -> ~1.2 GB (CSV)

Step 4: ricerca full-text e generazione CSV

grep -i "intelligenza artificiale" anac_cig_{anno}/*.csv > ia_results_{anno}.csv

Poi con uno script Python ho:

  1. Letto l'header originale dal primo file CSV dell'anno (61 campi, separatore ;)
  2. Pulito ogni riga dal prefisso del nome file aggiunto da grep (cig_csv_2025_01.csv:...)
  3. Scritto il CSV finale con header + tutti i record trovati

Risultati

Anno Record Importo complessivo
2023 111 € 44.105.241
2024 330 € 597.930.185
2025 518 € 143.273.323

File generati:

  • appalti_ia_2023_anac.csv (107 KB, 111 record, 61 campi)
  • appalti_ia_2024_anac.csv (327 KB, 330 record, 61 campi)
  • appalti_ia_2025_anac.csv (512 KB, 518 record, 61 campi)

Suggerimenti per lo sviluppatore di Borruso

1. User-Agent configurabile

Il problema principale e' che il WAF di ANAC (e probabilmente di altri portali governativi italiani) blocca le richieste con User-Agent non-browser. Sarebbe utile:

  • Permettere di configurare un User-Agent custom per server
  • Oppure usare di default un User-Agent che simuli un browser

2. Supporto richieste POST con JSON body

Alcuni portali CKAN (come ANAC) bloccano le richieste GET con query string ma accettano POST con body JSON. L'API CKAN supporta nativamente entrambi i metodi. Sarebbe utile:

  • Aggiungere un'opzione request_method: "POST" per server specifici
  • Oppure implementare un fallback automatico: se GET fallisce, riprovare con POST

3. Gestione errori WAF

Attualmente quando il WAF blocca la richiesta, il tool restituisce:

CKAN API returned success=false: "<html>..."

Sarebbe utile:

  • Riconoscere le risposte WAF/firewall (contengono "Request Rejected", "Access Denied", ecc.)
  • Restituire un messaggio di errore piu' specifico, es. "La richiesta e' stata bloccata dal firewall del server. Provare con un User-Agent diverso."

4. Fallback DataStore -> download risorsa

Quando datastore_search non e' disponibile (come su ANAC), il tool potrebbe suggerire o supportare il download diretto dei file risorsa tramite l'URL presente nei metadati del dataset.

5. Configurazione per-server

Un meccanismo di profili per server problematici sarebbe utile, ad esempio:

{
  "server_profiles": {
    "https://dati.anticorruzione.it/opendata": {
      "api_base": "/api/3",
      "user_agent": "Mozilla/5.0 ...",
      "request_method": "POST",
      "datastore_available": false
    }
  }
}

Ambiente di esecuzione

  • Tool: Claude Code (CLI) con MCP server Borruso
  • Data: 2026-01-28
  • OS: Linux 6.14.0-1019-oem

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions