A la lliçó anterior vam recórrer l'escaneig dinàmic de BazarNube a mà: explorar, escanejar passiu i actiu, interpretar alertes i generar un informe. Funciona, però no escala: ningú no obrirà la GUI de ZAP cada vegada que en Marc fa un push. La seguretat que només passa "quan algú se'n recorda" no és seguretat. En aquesta última lliçó del mòdul portem ZAP al pipeline de CI/CD: convertim l'escaneig en un pas automàtic que corre en cada canvi, publica l'informe com a artefacte i trenca el build quan apareixen vulnerabilitats per sobre del llindar acordat. Aquest és el DAST dins d'un flux DevSecOps.

Nota èticolegal (segueix vigent). Automatitzar no canvia les regles: l'escaneig actiu ataca de debò. El pipeline ha d'apuntar sempre al staging propi i efímer de BazarNube (https://staging.bazarnube.local), mai a producció ni a serveis de tercers. Automatitzar un atac contra un sistema aliè el fa més perillós, no menys.

Contingut

  1. Per què automatitzar el DAST
  2. Els scripts empaquetats: baseline scan i full scan amb Docker
  3. El fitxer de regles per gestionar falsos positius
  4. L'Automation Framework (YAML amb jobs)
  5. L'API i el mode daemon de ZAP
  6. Integració en GitHub Actions
  7. Integració en GitLab CI
  8. Llindars, fallada de build i informes com a artefactes
  9. Un pipeline DAST complet per a BazarNube
  10. Errors comuns i consells
  11. Exercicis
  12. Conclusió

Per què automatitzar el DAST

Automatitzar l'escaneig dinàmic persegueix tres objectius:

  • Repetibilitat: el mateix escaneig, amb la mateixa política, cada vegada. Sense passos oblidats.
  • Detecció primerenca: una vulnerabilitat trobada al pipeline costa molt menys que una trobada en producció.
  • Governança: el pipeline pot bloquejar un desplegament si apareix alguna cosa greu, aplicant una política objectiva en lloc d'un criteri individual.
flowchart LR
    A[Push / MR a BazarNube] --> B[Build i deploy a staging]
    B --> C[ZAP DAST en contenidor]
    C --> D{Alertes > llindar?}
    D -->|Si| E[Build FAIL, bloqueja deploy]
    D -->|No| F[Build OK, publica informe]
    E --> G[Informe artefacte]
    F --> G

Els scripts empaquetats: baseline scan i full scan amb Docker

ZAP publica scripts llestos per a CI dins de la imatge zaproxy/zap-stable (abans owasp/zap2docker-stable). Els dos que importen:

Script Què fa Ataca activament Durada Quan usar-lo
zap-baseline.py Spider + escaneig passiu; reporta capçaleres, cookies, fugues No Curt (minuts) En cada PR/commit, gate ràpid
zap-full-scan.py Spider + AJAX Spider + escaneig actiu complet Llarg (desenes de min) Nocturn o pre-release
zap-api-scan.py Escaneig guiat per definició OpenAPI/SOAP/GraphQL Variable APIs (la de BazarNube)

Estratègia per a BazarNube: zap-baseline.py en cada Pull Request (ràpid, no invasiu, apte com a gate) i zap-full-scan.py en un job nocturn contra l'staging dedicat (lent i agressiu, però fora del camí crític dels desenvolupadors).

Exemple de baseline contra staging, muntant un volum per recollir informes:

docker run --rm -v "$(pwd)/zap-out:/zap/wrk/:rw" \
  -t zaproxy/zap-stable zap-baseline.py \
  -t https://staging.bazarnube.local \
  -r baseline-report.html \
  -J baseline-report.json \
  -w baseline-report.md \
  -c bazarnube-rules.conf

Flags clau:

  • -t URL objectiu (sempre staging propi).
  • -r/-J/-w informes en HTML, JSON i Markdown.
  • -c fitxer de regles (falsos positius, veure a sota).
  • -I no fallar el build davant alertes de nivell Info (només retornar error per WARN/FAIL).
  • -l nivell mínim que provoca fallada (PASS, IGNORE, INFO, WARN, FAIL).

El full scan és idèntic en forma, canviant l'script:

docker run --rm -v "$(pwd)/zap-out:/zap/wrk/:rw" \
  -t zaproxy/zap-stable zap-full-scan.py \
  -t https://staging.bazarnube.local \
  -r full-report.html -J full-report.json \
  -c bazarnube-rules.conf

El fitxer de regles per gestionar falsos positius

A 06-03 vam fer triage manual marcant falsos positius a la GUI. En CI això no val: necessitem que la decisió sigui codi versionat. El fitxer de regles (-c) permet fixar el tractament de cada regla pel seu ID. Format: ID<TAB>acció<TAB>URL-regex(opcional).

# bazarnube-rules.conf
# ID	Accio	URL (opcional)
10096	IGNORE	# Timestamp Disclosure: soroll, no aplica a BazarNube
10021	IGNORE	# X-Content-Type-Options en assets estatics del CDN
10035	WARN	# Strict-Transport-Security: avisar, no trencar build encara
40012	FAIL	# Reflected XSS: sempre trenca el build
40018	FAIL	# SQL Injection: sempre trenca el build

Accions possibles: IGNORE (no reportar), WARN (reportar sense fallar el build) i FAIL (reportar i trencar el build). Així el tractament dels falsos positius viu al repositori, es revisa en un PR i queda auditat: res de decisions invisibles.

L'Automation Framework (YAML amb jobs)

Els scripts són còmodes però rígids. Per a escaneigs amb context, autenticació i política a mida (com els que vam definir a 06-02 i 06-03), ZAP ofereix l'Automation Framework: un únic fitxer YAML que descriu l'escaneig com una llista de jobs encadenats. És la forma recomanada per a casos seriosos.

# zap-bazarnube.yaml
env:
  contexts:
    - name: BazarNube
      urls:
        - https://staging.bazarnube.local
      includePaths:
        - "https://staging.bazarnube.local.*"
      excludePaths:
        - "https://staging.bazarnube.local/logout.*"
        - ".*pay\\.tercero\\.com.*"
      authentication:
        method: form
        parameters:
          loginPageUrl: https://staging.bazarnube.local/login
          loginRequestUrl: https://staging.bazarnube.local/api/auth/login
          loginRequestBody: "username={%username%}&password={%password%}"
      users:
        - name: venedor
          credentials:
            username: [email protected]
            password: ${BZN_TEST_PASS}
  parameters:
    failOnError: true

jobs:
  - type: spider
    parameters:
      context: BazarNube
      user: venedor
  - type: spiderAjax
    parameters:
      context: BazarNube
      user: venedor
  - type: passiveScan-wait
  - type: activeScan
    parameters:
      context: BazarNube
      user: venedor
      policy: BazarNube-Web
  - type: report
    parameters:
      template: traditional-html
      reportFile: zap-bazarnube.html
  - type: report
    parameters:
      template: traditional-json
      reportFile: zap-bazarnube.json

S'executa amb:

docker run --rm -v "$(pwd):/zap/wrk/:rw" \
  -t zaproxy/zap-stable zap.sh -cmd -autorun /zap/wrk/zap-bazarnube.yaml

Fixa't que la contrasenya de prova arriba per variable d'entorn (${BZN_TEST_PASS}), injectada des del gestor de secrets del CI: mai credencials al YAML.

L'API i el mode daemon de ZAP

Per a control total (integrar ZAP amb scripts propis, orquestrar des de Python/Node), s'arrenca ZAP com a daemon sense GUI i se li parla per la seva API REST:

# ZAP headless escoltant al 8080, amb clau d'API obligatoria
docker run -u zap -p 8080:8080 -d --name zap zaproxy/zap-stable \
  zap.sh -daemon -host 0.0.0.0 -port 8080 \
  -config api.key=$ZAP_KEY \
  -config api.addrs.addr.name=.* -config api.addrs.addr.regex=true
  • -daemon arrenca sense interfície gràfica.
  • -config api.key=... protegeix l'API amb una clau (obligatòria; no la deixis buida).
  • Endpoints REST sota /JSON/... per llançar spider, active scan, llegir alertes i generar informes (ja els vam usar a 06-03).

Molts equips fan servir la llibreria client oficial en lloc de curl cru:

from zapv2 import ZAPv2
zap = ZAPv2(apikey='CLAU', proxies={'http': 'http://localhost:8080'})
zap.spider.scan('https://staging.bazarnube.local')
zap.ascan.scan('https://staging.bazarnube.local', scanpolicyname='BazarNube-Web')
alerts = zap.core.alerts(baseurl='https://staging.bazarnube.local')

Integració en GitHub Actions

A GitHub, el flux típic és aixecar staging, executar el baseline en cada PR i pujar l'informe com a artefacte. Existeix l'acció oficial zaproxy/action-baseline:

# .github/workflows/dast.yml
name: DAST BazarNube
on: [pull_request]

jobs:
  zap-baseline:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Aixecar staging de BazarNube
        run: docker compose -f docker-compose.staging.yml up -d --wait

      - name: ZAP Baseline Scan
        uses: zaproxy/[email protected]
        with:
          target: 'https://staging.bazarnube.local'
          rules_file_name: 'bazarnube-rules.conf'
          cmd_options: '-J baseline-report.json -w baseline-report.md'
          fail_action: true

      - name: Publicar informe com a artefacte
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: zap-report
          path: |
            report_html.html
            baseline-report.json
            baseline-report.md

fail_action: true fa que les alertes marcades com a FAIL trenquin el job (i per tant bloquegin el merge si el check és obligatori). El pas upload-artifact amb if: always() garanteix que l'informe es guarda encara que el build falli, que és just quan més el necessites.

Integració en GitLab CI

A GitLab el patró és equivalent fent servir la imatge de ZAP com a imatge del job:

# .gitlab-ci.yml
dast_baseline:
  stage: test
  image: zaproxy/zap-stable
  variables:
    TARGET: "https://staging.bazarnube.local"
  script:
    - mkdir -p zap-out
    - zap-baseline.py -t "$TARGET"
        -c bazarnube-rules.conf
        -r zap-out/report.html -J zap-out/report.json
        || EXIT=$?
    - echo "ZAP exit code $EXIT"
    - '[ "${EXIT:-0}" -lt 2 ] || exit 1'   # 2 = WARN, 3 = FAIL -> trenca build
  artifacts:
    when: always
    paths:
      - zap-out/
    expire_in: 30 days

Aquí gestionem el codi de sortida a mà: ZAP retorna 0 (tot OK), 1 (error intern), 2 (hi ha WARN) i 3 (hi ha FAIL). Decidim el llindar a la mateixa lògica del job. artifacts: when: always compleix la mateixa funció que a GitHub: conservar l'informe encara que el job falli.

Llindars, fallada de build i informes com a artefactes

La política de "quan trencar el build" és una decisió d'equip, no tècnica. Una progressió sana per a BazarNube:

Fase d'adopció Què trenca el build Objectiu
Introducció Res (WARN en tot) Guanyar visibilitat sense frustrar l'equip
Consolidació Només High confirmades (SQLi, XSS) Bloquejar el greu, tolerar el soroll
Maduresa High i Medium segons regles Elevar el llistó progressivament

Codis de sortida dels scripts, per configurar el gate:

Codi Significat Acció recomanada en CI
0 Sense troballes per sobre del llindar Continuar
1 Error d'execució de ZAP Fallar i investigar
2 Almenys un WARN Segons política (avisar o fallar)
3 Almenys un FAIL Fallar el build

I sempre publica els informes (HTML per a humans, JSON per comparar entre builds, Markdown per enganxar al ticket) com a artefactes amb if: always() / when: always.

Un pipeline DAST complet per a BazarNube

Ajuntant-ho tot, l'estratègia de BazarNube combina dos gates de cost diferent:

flowchart TD
    A[PR obert] --> B[Deploy efimer a staging]
    B --> C[zap-baseline.py amb rules.conf]
    C --> D{FAIL?}
    D -->|Si| E[Bloqueja merge + artefacte]
    D -->|No| F[Merge permes + artefacte]
    G[Cron nocturn] --> H[Deploy staging estable]
    H --> I[zap-full-scan.py autenticat]
    I --> J[Informe al backlog: BZN-xxx]
  • Per PR: zap-baseline.py (ràpid, no invasiu) com a gate obligatori. Només trenca el build davant FAIL (SQLi/XSS confirmades).
  • Nocturn: zap-full-scan.py o l'Automation Framework autenticat (lent, agressiu) contra staging dedicat. No bloqueja ningú; les seves troballes entren com a tickets BZN-xxx al backlog, ja mapejats al Top Ten (06-03).

Així el desenvolupador té feedback ràpid al seu PR i l'escaneig profund passa sense frenar el flux de treball. L'SRE manté el staging efímer; la Lucía i en Marc reben tickets accionables en lloc d'un PDF enorme cada trimestre.

Errors Comuns i Consells

  • Escanejar un staging que no està llest. Si el DAST arrenca abans que l'app respongui, no explora res. Fes servir --wait/healthchecks abans de llançar ZAP.
  • Deixar l'API sense clau. -config api.key= buit exposa el daemon. Genera una clau i passa-la per secret del CI.
  • Credencials o claus al YAML. Tot secret (contrasenya de prova, ZAP_KEY) va per variables/secrets del CI, mai versionat.
  • Posar el full scan com a gate de cada PR. És massa lent i agressiu; frustra l'equip i alenteix el merge. Baseline en PR, full scan nocturn.
  • No publicar l'informe quan el build falla. Sense if: always()/when: always perds just l'informe de la fallada.
  • Gestionar falsos positius "a ull" cada execució. Posa'ls al fitxer de regles versionat; que es revisin en un PR.
  • Posar el llistó al màxim des del dia u. Si tot trenca el build de cop, l'equip desactivarà l'escaneig. Puja el llindar per fases.

Exercicis

Exercici 1. En Marc proposa posar zap-full-scan.py com a gate obligatori en cada Pull Request de BazarNube. Explica per què no és bona idea i quina estratègia de dos nivells proposaries en el seu lloc.

Exercici 2. El baseline reporta insistentment l'alerta 10096 (Timestamp Disclosure), que l'equip ja ha verificat com a fals positiu. Escriu la línia del fitxer bazarnube-rules.conf per silenciar-la i explica per què això és millor que marcar-ho a la GUI.

Exercici 3. A GitLab, el teu job de ZAP acaba amb codi de sortida 3 però el pipeline apareix en verd. Què està passant i com ho corregeixes perquè aquest cas trenqui el build i conservi l'informe?

Solucions

Solució 1. El full scan fa escaneig actiu complet amb Spider i AJAX Spider: triga desenes de minuts i ataca de debò, per la qual cosa com a gate de cada PR alentiria el desenvolupament i generaria fricció. Estratègia recomanada: baseline en cada PR (ràpid, passiu, trenca el build només davant FAIL) i full scan nocturn contra un staging dedicat, les troballes del qual entren al backlog com a tickets sense bloquejar ningú.

Solució 2. Línia del fitxer de regles:

10096	IGNORE	# Timestamp Disclosure: fals positiu verificat per l'equip

És millor que la GUI perquè la decisió queda versionada al repositori: es revisa en un PR, té autor i data, s'aplica idèntica en cada execució del CI i és auditable. Un Mark as False Positive a la GUI viu només a la sessió local d'una persona i no es propaga al pipeline.

Solució 3. L'script retorna 3 (hi ha alertes FAIL), però el job no propaga aquest codi perquè probablement es va capturar amb || true o no es tradueix a exit 1. Cal avaluar el codi de sortida i forçar la fallada, a més de conservar artefactes sempre:

script:
  - zap-baseline.py -t "$TARGET" -c bazarnube-rules.conf -r zap-out/report.html || EXIT=$?
  - '[ "${EXIT:-0}" -lt 2 ] || exit 1'
artifacts:
  when: always
  paths: [ zap-out/ ]

Amb [ "${EXIT:-0}" -lt 2 ] || exit 1, qualsevol codi >= 2 (WARN/FAIL) trenca el build, i when: always garanteix que l'informe es puja fins i tot en la fallada.

Conclusió

Amb aquesta lliçó tanques el Mòdul 6. Has passat d'executar ZAP a mà a integrar-lo en un pipeline DevSecOps: baseline i full scan amb Docker, l'Automation Framework per a escaneigs autenticats a mida, l'API en mode daemon, pipelines reals en GitHub Actions i GitLab CI, gestió de llindars i fallada de build, informes com a artefactes i un fitxer de regles versionat per als falsos positius. El resultat és un DAST que corre sol, en cada canvi, i protegeix BazarNube sense dependre que algú se'n recordi.

Aquest és també el punt on tot el curs convergeix. ZAP no viu aïllat: és una peça dins d'un SDLC segur. Al Mòdul 7 fem aquest salt: el cicle de vida de desenvolupament segur, el modelatge d'amenaces per decidir què protegir abans d'escriure codi, i DevSecOps com a filosofia que fila tot l'après. Allà encaixaràs les peces: el Top Ten 2021 (M3) com a catàleg de riscos, l'ASVS (M4) com a requisits verificables, SAMM (M5) com a model de maduresa del programa, i ZAP (M6) com la verificació dinàmica automatitzada dins del pipeline. La seguretat deixa de ser una fase final per convertir-se en una propietat contínua del procés.

Curs d'OWASP: Directrius i Estàndards per a la Seguretat en Aplicacions Web

Mòdul 1: Introducció a OWASP

Mòdul 2: Principals Projectes d'OWASP

Mòdul 3: OWASP Top Ten 2021 en Profunditat

Mòdul 4: OWASP ASVS (Application Security Verification Standard)

Mòdul 5: OWASP SAMM (Software Assurance Maturity Model)

Mòdul 6: OWASP ZAP (Zed Attack Proxy)

Mòdul 7: Bones Pràctiques i Recomanacions

Mòdul 8: Exercicis Pràctics i Casos d'Estudi

Mòdul 9: Avaluació i Certificació

© Copyright 2026. Tots els drets reservats