El pla de resposta de 04-04 acabava sempre al mateix lloc. La Marta identifica el patró de
l'atac —peticions a /buscar amb paràmetres aleatoris, des de 8.400 adreces de 61 països, amb un
agent d'usuari falsificat idèntic en el 91 % dels casos— i llavors necessita una eina capaç
d'actuar sobre això. No sobre una IP. No sobre un port. Sobre el contingut de la petició
HTTP.
Ni sg-mercadofresco-tienda ni la NACL de les subxarxes públiques ho poden fer, i no per falta de
funcions: és que treballen en una altra capa. Un grup de seguretat veu un paquet TCP cap al port 443
i no té manera de saber si a dins hi va una consulta d'un client de Getafe o una injecció SQL.
Shield, per la seva banda, treballa amb volum i amb paquets malformats: un atac compost per peticions
HTTP perfectament vàlides li resulta invisible.
AWS WAF (Web Application Firewall) és el tallafocs que sí que mira a dins. Inspecciona el
mètode, la ruta, la cadena de consulta, les capçaleres, les galetes i el cos de cada petició, i
decideix. Aquesta lliçó el munta davant de la distribució E2QWERTY123ABC i de
l'alb-mercadofresco-tienda, i tanca amb això el mòdul de seguretat.
Advertiment. L'enfocament d'aquesta lliçó és estrictament defensiu: protegir la teva pròpia aplicació. Els exemples són didàctics i estan simplificats. Qualsevol configuració de WAF que hagi d'aplicar-se a un entorn real amb dades de clients —especialment si hi intervenen el RGPD o PCI DSS— ha de ser revisada per un professional de seguretat o de compliment abans de posar-se en producció. Una regla mal calibrada bloqueja clients legítims i pot costar més que l'atac del qual pretén protegir-te; el procediment de desplegament que es descriu aquí no és una recomanació opcional.
Contingut
- Què veu WAF que un grup de seguretat no pot veure
- On s'associa una Web ACL
- Components: Web ACL, regles i grups de regles
- Unitats de capacitat (WCU)
- Accions i prioritat d'avaluació
- El recorregut d'una petició
- Declaracions de coincidència
- Transformacions de text
- Grups de regles gestionades per AWS
- El mètode de desplegament:
Countprimer,Blockdesprés - Regles basades en taxa
- La Web ACL de MercadoFresco, completa
- Associar la Web ACL a CloudFront i a l'ALB
- Registres de WAF i la seva anàlisi
- Mètriques i mostres de peticions bloquejades
- Falsos positius: diagnòstic i exclusions
- Cost i càlcul per a MercadoFresco
- Neteja
- La postura de seguretat de MercadoFresco
- El que falta: ningú no està mirant
Què veu WAF que un grup de seguretat no pot veure
| Grup de seguretat | NACL | AWS WAF | |
|---|---|---|---|
| Capa OSI | 3/4 | 3/4 | 7 (aplicació) |
| Inspecciona | IP, protocol, port | IP, protocol, port | Mètode, ruta, capçaleres, galetes, cos, consulta |
| Estat | Amb estat | Sense estat | Amb estat per petició |
| S'aplica a | ENI (instància, ALB, RDS) | Subxarxa | CloudFront, ALB, API Gateway, AppSync, Cognito |
| Detecta injecció SQL | No | No | Sí |
| Bloqueja per país | No | No | Sí |
| Limita peticions per IP | No | No | Sí |
| Distingeix navegador de bot | No | No | Sí |
| Cost | Gratis | Gratis | 5 USD/mes + regles + peticions |
Un exemple concret que resumeix la diferència. Aquesta petició:
Per a sg-mercadofresco-alb això és una connexió TCP al port 443 des d'una IP qualsevol: la
permet, perquè és exactament el que ha de permetre. Per a WAF és una petició amb un patró
d'injecció SQL a la cadena de consulta i una eina d'atac declarada a l'agent d'usuari: la bloqueja.
Les tres eines són complementàries i cap no en substitueix una altra:
| Amenaça | Eina |
|---|---|
| Accés al port 5432 de la base de dades des d'internet | Grup de seguretat (03-02) |
| Inundació volumètrica de 200 Gbps | Shield (04-04) |
| Injecció SQL, XSS, scripts maliciosos | WAF |
Inundació HTTP contra /buscar |
WAF amb regla per taxa |
| Credencials robades i utilitzades correctament | IAM (04-01), no hi ha tallafocs que ajudi |
On s'associa una Web ACL
| Servei | Àmbit | Notes |
|---|---|---|
| CloudFront | CLOUDFRONT |
La Web ACL s'ha de crear a us-east-1 |
| Application Load Balancer | REGIONAL |
A la regió de l'ALB |
| API Gateway (REST) | REGIONAL |
Per etapa |
| AppSync | REGIONAL |
GraphQL |
| Cognito | REGIONAL |
Grups d'usuaris |
| App Runner, Verified Access | REGIONAL |
Menys habituals |
La primera fila amaga el parany que més temps fa perdre: una Web ACL per a CloudFront es crea a
us-east-1 amb --scope CLOUDFRONT, sense importar on sigui la resta de la teva infraestructura.
És la mateixa regla que ja vam veure amb els certificats d'ACM a 03-04 i amb les mètriques de
CloudFront a 04-04. Una Web ACL regional creada a eu-west-1 no es pot associar a una distribució.
Per a MercadoFresco muntem dues Web ACL:
| Web ACL | Àmbit | Regió | Protegeix |
|---|---|---|---|
waf-mercadofresco-cdn |
CLOUDFRONT |
us-east-1 |
Distribució E2QWERTY123ABC |
waf-mercadofresco-alb |
REGIONAL |
eu-west-1 |
alb-mercadofresco-tienda |
Per què dues, si tot el trànsit passa per CloudFront? Per defensa en profunditat. La de l'ALB és la xarxa de seguretat per si algú aconsegueix saltar-se la vora, i protegeix també el trànsit intern o de proves que arribi directament. És el mateix raonament pel qual a 04-04 vam tancar l'ALB amb la capçalera verificada a més de la llista de prefixos.
Components: Web ACL, regles i grups de regles
flowchart TD
A["Web ACL<br/>waf-mercadofresco-cdn"] --> B["Regla 1 - prioritat 0<br/>IP set: bloqueig permanent"]
A --> C["Regla 2 - prioritat 10<br/>Grup gestionat:<br/>AmazonIpReputationList"]
A --> D["Regla 3 - prioritat 20<br/>Grup gestionat:<br/>CommonRuleSet"]
A --> E["Regla 4 - prioritat 30<br/>Grup gestionat:<br/>SQLiRuleSet"]
A --> F["Regla 5 - prioritat 40<br/>Per taxa: /login"]
A --> G["Regla 6 - prioritat 50<br/>Per taxa: /api/pedidos"]
A --> H["Accio per defecte:<br/>ALLOW"]
Els quatre conceptes:
- Web ACL (Web Access Control List): el contenidor. Té una llista ordenada de regles i una acció per defecte que s'aplica al que no coincideix amb cap.
- Regla: una declaració de coincidència més una acció. Té una prioritat numèrica.
- Grup de regles: un conjunt reutilitzable de regles. Poden ser gestionats per AWS, gestionats per tercers (del Marketplace) o propis.
- IP set i regex pattern set: llistes reutilitzables d'adreces o d'expressions regulars que les regles referencien per ARN.
L'acció per defecte defineix el model:
| Acció per defecte | Model | Quan |
|---|---|---|
Allow |
Llista negra: es permet tot llevat del que coincideix amb una regla | Llocs públics com una botiga |
Block |
Llista blanca: es bloqueja tot llevat del que es permet explícitament | Taulers interns, API privades |
MercadoFresco fa servir Allow per defecte a la botiga pública. Per a admin.mercadofresco.example,
el subdomini d'administració que vam crear a 03-05, el model correcte seria Block per defecte amb
una regla que només permeti la IP de l'oficina 192.168.10.0/24 i el seu rang de sortida públic.
Unitats de capacitat (WCU)
Cada regla consumeix WCU (Web ACL Capacity Units), una mesura del cost de còmput d'avaluar-la. Una Web ACL té un límit de 1.500 WCU per defecte (ampliable a 5.000 si se sol·licita).
| Element | WCU aproximades |
|---|---|
| Coincidència d'IP set | 1 |
| Coincidència de cadena simple | 1-5 |
| Cada transformació de text | +10 per cadascuna |
| Expressió regular | 25-35 |
| Coincidència geogràfica | 1 |
| Regla per taxa | 2 |
AWSManagedRulesCommonRuleSet |
700 |
AWSManagedRulesKnownBadInputsRuleSet |
200 |
AWSManagedRulesSQLiRuleSet |
200 |
AWSManagedRulesLinuxRuleSet |
200 |
AWSManagedRulesAmazonIpReputationList |
25 |
AWSManagedRulesAnonymousIpList |
50 |
AWSManagedRulesBotControlRuleSet |
50 |
El pressupost s'esgota abans del que sembla: CommonRuleSet tot sol en consumeix gairebé la meitat.
Convé planificar-ho:
aws wafv2 describe-managed-rule-group \
--vendor-name AWS --name AWSManagedRulesCommonRuleSet \
--scope CLOUDFRONT --region us-east-1 \
--query '{Capacitat:Capacity, Regles:Rules[].Name}' \
--profile mercadofresco-devI consultar quant en portes consumit:
aws wafv2 get-web-acl --name waf-mercadofresco-cdn --scope CLOUDFRONT \
--id <id> --region us-east-1 --query 'WebACL.Capacity' --profile mercadofresco-devAccions i prioritat d'avaluació
| Acció | Què fa | Cost per al client |
|---|---|---|
Allow |
Permet la petició i atura l'avaluació | Cap |
Block |
Rebutja amb 403 (personalitzable) i atura l'avaluació | Petició perduda |
Count |
Compta i continua avaluant. No bloqueja res | Cap |
CAPTCHA |
Mostra un repte visual; si es resol, emet un testimoni vàlid uns minuts | Fricció alta |
Challenge |
Repte silenciós de JavaScript; el navegador el resol sol | Fricció gairebé nul·la |
Dos comportaments que cal tenir molt clars:
AllowiBlocksón terminals. Tan bon punt una regla coincideix amb una d'aquestes dues accions, l'avaluació s'atura i no es miren les regles següents. Per això la prioritat importa tant.Countmai no és terminal. Registra la coincidència i continua. És la base del mètode de desplegament segur.
Sobre CAPTCHA enfront de Challenge: Challenge és gairebé sempre l'opció correcta. Verifica que
hi ha un navegador real executant JavaScript sense molestar l'usuari, filtra la pràctica totalitat
dels bots simples i no fa mal a la conversió. CAPTCHA reserva'l per a accions crítiques —/login
després de diversos intents fallits, o el pas final del pagament si detectes frau— perquè cada
CAPTCHA mostrat a un client real és un percentatge de vendes perdudes.
La prioritat s'avalua de menor a major número. No han de ser consecutius: fer servir 0, 10, 20, 30 en lloc de 0, 1, 2, 3 permet inserir regles després sense renumerar-ho tot.
El recorregut d'una petició
flowchart TD
A["Peticio HTTPS d'un client"] --> B{"Prioritat 0<br/>IP a la llista de bloqueig?"}
B -->|"Si"| Z["BLOCK 403 - fi"]
B -->|"No"| C{"Prioritat 10<br/>IP amb mala reputacio?"}
C -->|"Si"| Z
C -->|"No"| D{"Prioritat 20<br/>CommonRuleSet: XSS, rutes,<br/>mida, agents dolents?"}
D -->|"Si"| Z
D -->|"No"| E{"Prioritat 30<br/>Injeccio SQL?"}
E -->|"Si"| Z
E -->|"No"| F{"Prioritat 40<br/>Mes de 100 peticions a /login<br/>en 5 min des d'aquesta IP?"}
F -->|"Si"| Y["CAPTCHA"]
F -->|"No"| G{"Prioritat 50<br/>Mes de 2000 a /api/pedidos<br/>en 5 min?"}
G -->|"Si"| Z
G -->|"No"| H["Accio per defecte: ALLOW"]
H --> I["CloudFront serveix<br/>des de memoria cau o origen"]
Fixa't en l'ordre, que no és casual:
- El més barat i més segur primer: comprovar una IP en un conjunt costa 1 WCU i no té falsos positius.
- Les regles gestionades al mig: cares en WCU però molt eficaces.
- Les regles per taxa al final: només té sentit comptar peticions que ja han superat tots els filtres anteriors.
Declaracions de coincidència
| Declaració | Què compara | Exemple d'ús |
|---|---|---|
| ByteMatch | Una cadena en un component de la petició | L'agent d'usuari conté sqlmap |
| RegexPatternSet | Una expressió regular | Rutes que encaixen amb ^/admin/.* |
| SizeConstraint | Mida d'un component | Cos més gran de 8 KB a /api/pedidos |
| GeoMatch | País d'origen (per GeoIP) | Bloquejar països sense clients |
| IPSet | IP o rang en una llista | Permetre sempre l'oficina |
| SqliMatch | Patrons d'injecció SQL | Cadena de consulta i cos |
| XssMatch | Patrons de scripting entre llocs | Formularis |
| LabelMatch | Etiqueta posada per una regla anterior | Combinar regles gestionades amb lògica pròpia |
| RateBased | Peticions per finestra de temps | Protegir /login |
| And / Or / Not | Combinació lògica | «Espanya i ruta /admin» |
Els components de la petició que es poden inspeccionar: UriPath, QueryString,
SingleHeader, AllHeaders, Cookies, Method, Body (primers 8 KB a l'ALB, fins a 64 KB a
CloudFront amb configuració), JsonBody (amb anàlisi de JSON) i SingleQueryArgument.
Exemple d'una regla pròpia que bloqueja l'accés a /admin des de fora d'Espanya:
{
"Name": "AdminSoloDesdeEspana",
"Priority": 5,
"Statement": {
"AndStatement": {
"Statements": [
{
"ByteMatchStatement": {
"SearchString": "/admin",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [
{ "Priority": 0, "Type": "LOWERCASE" },
{ "Priority": 1, "Type": "URL_DECODE" }
],
"PositionalConstraint": "STARTS_WITH"
}
},
{
"NotStatement": {
"Statement": {
"GeoMatchStatement": { "CountryCodes": ["ES"] }
}
}
}
]
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AdminSoloDesdeEspana"
}
}AndStatement exigeix que es compleixin les dues condicions: la ruta comença per /admin i el
país no és Espanya. PositionalConstraint admet EXACTLY, STARTS_WITH, ENDS_WITH, CONTAINS i
CONTAINS_WORD.
Un avís sobre GeoMatchStatement: la geolocalització per IP no és infal·lible i una VPN l'esquiva
sense esforç. Serveix per reduir soroll de fons, no com a control d'accés seriós. I compte amb
bloquejar països: un client espanyol de vacances a França deixaria de poder comprar.
Transformacions de text
Són imprescindibles i s'obliden constantment. Una regla que busca la cadena <script> no
coincideix amb cap d'aquestes variants, que fan exactament el mateix:
| Variant | Tècnica |
|---|---|
<SCRIPT> |
Majúscules |
%3Cscript%3E |
Codificació d'URL |
<scr\x00ipt> |
Byte nul inserit |
<script > |
Espais addicionals |
<script> |
Entitats HTML |
<scr<script>ipt> |
Imbricació |
Les transformacions de text normalitzen el valor abans de comparar-lo:
| Transformació | Què fa |
|---|---|
NONE |
Res |
LOWERCASE |
Tot a minúscules |
URL_DECODE |
Descodifica %3C a < |
HTML_ENTITY_DECODE |
Descodifica < a < |
COMPRESS_WHITE_SPACE |
Col·lapsa espais múltiples en un |
REMOVE_NULLS |
Elimina bytes nuls |
CMD_LINE |
Normalitza sintaxi de línia d'ordres |
BASE64_DECODE |
Descodifica base64 |
NORMALIZE_PATH |
Resol ../ i // a les rutes |
S'apliquen en ordre de Priority i es poden encadenar. La combinació defensiva estàndard:
"TextTransformations": [
{ "Priority": 0, "Type": "URL_DECODE" },
{ "Priority": 1, "Type": "HTML_ENTITY_DECODE" },
{ "Priority": 2, "Type": "LOWERCASE" },
{ "Priority": 3, "Type": "REMOVE_NULLS" },
{ "Priority": 4, "Type": "COMPRESS_WHITE_SPACE" }
]Una regla sense transformacions de text és una regla que s'esquiva escrivint en majúscules. Cada transformació costa 10 WCU, i aquest és el preu que la regla serveixi per a alguna cosa.
Els grups de regles gestionades per AWS ja inclouen les transformacions adequades: és una de les raons per començar per ells.
Grups de regles gestionades per AWS
AWS manté i actualitza aquests conjunts. La majoria són gratuïts: només es paguen les WCU que consumeixen dins de la teva Web ACL, no un càrrec a part.
| Grup | WCU | Cost | Què protegeix | Per a MercadoFresco? |
|---|---|---|---|---|
AWSManagedRulesCommonRuleSet |
700 | Gratis | Base OWASP: XSS, rutes malicioses, mides anòmales, agents d'usuari buits | Sí, essencial |
AWSManagedRulesKnownBadInputsRuleSet |
200 | Gratis | Entrades de vulnerabilitats conegudes (Log4Shell, deserialització) | Sí, essencial |
AWSManagedRulesSQLiRuleSet |
200 | Gratis | Injecció SQL a consulta, cos i galetes | Sí: hi ha PostgreSQL al darrere |
AWSManagedRulesLinuxRuleSet |
200 | Gratis | LFI, inclusió d'/etc/passwd, execució d'ordres |
Sí: les instàncies són Linux |
AWSManagedRulesUnixRuleSet |
100 | Gratis | Ordres de shell | Redundant amb l'anterior |
AWSManagedRulesWindowsRuleSet |
200 | Gratis | PowerShell, ordres de Windows | No: no hi ha Windows |
AWSManagedRulesPHPRuleSet |
100 | Gratis | Injecció de PHP | Només si la botiga fa servir PHP |
AWSManagedRulesWordPressRuleSet |
100 | Gratis | Vulnerabilitats de WordPress | No |
AWSManagedRulesAmazonIpReputationList |
25 | Gratis | IP amb activitat maliciosa coneguda i nodes de xarxes de bots | Sí, barata i molt eficaç |
AWSManagedRulesAnonymousIpList |
50 | Gratis | VPN, Tor, servidors intermediaris, sortides de núvols | Amb compte: hi ha clients legítims amb VPN |
AWSManagedRulesBotControlRuleSet |
50 | 10 USD/mes + 1 USD/milió | Classifica bots: cercadors, rastrejadors, eines | Avaluar més endavant |
AWSManagedRulesATPRuleSet |
50 | 10 USD/mes + 1 USD/1.000 intents | Account Takeover Prevention: farciment de credencials, credencials filtrades | Interessant per a /login |
AWSManagedRulesACFPRuleSet |
50 | De pagament | Prevenció de registre fraudulent de comptes | No de moment |
Dos matisos sobre els de pagament:
- Bot Control té dos nivells: el comú, que identifica bots que es declaren (cercadors, monitoratge), i el dirigit, que detecta bots que es fan passar per navegadors fent servir empremtes digitals i reptes. El dirigit és notablement més car i només compensa amb un problema real de scraping o de revenda.
- ATP comprova les credencials de cada intent d'accés contra una base de dades de credencials filtrades, i detecta patrons de farciment. Per a MercadoFresco, amb comptes de clients que guarden adreces de repartiment, és la primera ampliació a considerar quan hi hagi pressupost.
Comença sempre pels gratuïts. CommonRuleSet + KnownBadInputs + SQLi + IpReputationList
són 1.125 WCU, gratis, i cobreixen la immensa majoria del que veuràs.
El mètode de desplegament: Count primer, Block després
Aquest apartat és el més important de la lliçó. Activar CommonRuleSet en mode Block un divendres
a la tarda i descobrir que una de les seves regles bloqueja el formulari de comanda és un desastre
autoinfligit pitjor que qualsevol atac.
El procediment correcte té quatre fases:
flowchart TD
A["Fase 1: crear la Web ACL amb TOTES<br/>les regles en Count i accio per defecte Allow"] --> B["Fase 2: deixar-la 7-14 dies,<br/>cobrint com a minim dos divendres"]
B --> C["Fase 3: analitzar els registres<br/>Quines regles compten?<br/>Contra quines peticions reals?"]
C --> D{"Hi ha falsos<br/>positius?"}
D -->|"Si"| E["Afegir exclusions de regles<br/>concretes o afinar l'abast"]
E --> C
D -->|"No"| F["Fase 4: passar a Block<br/>d'una en una, comencant<br/>per la mes segura"]
F --> G["Vigilar 24-48 h<br/>despres de cada canvi"]
G --> H["Web ACL en produccio"]
Per què cada fase:
- Fase 1.
Countregistra la coincidència i deixa passar la petició. Risc zero: si t'equivoques, no passa res. - Fase 2. Dues setmanes inclouen els dos pics de divendres i, amb sort, alguna campanya. Un desplegament basat en tres dies de trànsit de dimarts no veu els casos rars, i els casos rars són precisament els falsos positius.
- Fase 3. Els registres diuen exactament quina regla hauria bloquejat quina petició real, amb el seu URI, les seves capçaleres i la seva IP. Aquesta és la informació que no es pot endevinar.
- Fase 4. D'una en una. Si alguna cosa es trenca, saps exactament quina va ser.
Ordre recomanat per passar a Block, de menys a més risc de fals positiu:
| Ordre | Regla | Risc |
|---|---|---|
| 1 | IP set de bloqueig propi | Nul: l'has posat tu |
| 2 | AmazonIpReputationList |
Molt baix |
| 3 | SQLiRuleSet |
Baix, si no tens formularis amb SQL legítim |
| 4 | KnownBadInputsRuleSet |
Baix |
| 5 | Regles per taxa | Mitjà: cal calibrar el llindar |
| 6 | LinuxRuleSet |
Mitjà |
| 7 | CommonRuleSet |
El més alt: és el més ampli |
| 8 | AnonymousIpList |
Alt: bloqueja clients amb VPN |
Una regla en Count dins d'un grup gestionat es configura amb RuleActionOverrides, que permet
passar a Count regles individuals del grup sense desactivar-lo sencer:
{
"Name": "ConjuntoComun",
"Priority": 20,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"RuleActionOverrides": [
{ "Name": "SizeRestrictions_BODY", "ActionToUse": { "Count": {} } },
{ "Name": "NoUserAgent_HEADER", "ActionToUse": { "Count": {} } }
]
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "ConjuntoComun"
}
}Dos camps que es confonen sempre:
OverrideAction(només en grups de regles):{"None": {}}respecta les accions del grup;{"Count": {}}posa tot el grup en mode compte. Durant la fase 1 es fa servirCount.RuleActionOverrides: canvia l'acció de regles concretes dins del grup. És l'eina fina, la que es fa servir a la fase 3 per neutralitzar un fals positiu sense renunciar a la resta del grup.
Regles basades en taxa
Compten les peticions que coincideixen amb una condició dins d'una finestra de temps i actuen quan se supera un llindar. Són la resposta directa a l'atac de 04-04.
| Paràmetre | Valors | Comentari |
|---|---|---|
Limit |
10 a 2.000.000.000 | Peticions per finestra |
EvaluationWindowSec |
60, 120, 300, 600 | Per defecte 300 (5 minuts) |
AggregateKeyType |
IP, FORWARDED_IP, CUSTOM_KEY, CONSTANT |
Com s'agrupa el recompte |
ScopeDownStatement |
Qualsevol declaració | A quines peticions s'aplica el recompte |
AggregateKeyType mereix explicació:
IP: per adreça d'origen. L'habitual.FORWARDED_IP: fa servirX-Forwarded-For. Imprescindible si el WAF és a l'ALB darrere de CloudFront, perquè si no, totes les peticions semblaran venir de les IP de CloudFront i comptaran com una sola font.CUSTOM_KEY: agrupa per galeta de sessió, capçalera, paràmetre de consulta o combinació. Permet limitar «per compte d'usuari» en lloc de «per IP», molt més precís davant d'atacants distribuïts.CONSTANT: compta tot junt, sense agrupar. Serveix per posar un sostre global a una ruta cara.
ScopeDownStatement és la peça que la fa útil: sense ell, la regla comptaria totes les peticions
del lloc, i un client que navega pel catàleg generaria centenars de peticions legítimes. Amb ell,
només compten les que van a la ruta protegida.
Protecció de /login amb CAPTCHA en lloc de bloqueig:
{
"Name": "LimiteLogin",
"Priority": 40,
"Statement": {
"RateBasedStatement": {
"Limit": 100,
"EvaluationWindowSec": 300,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/login",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": { "Captcha": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "LimiteLogin"
}
}100 intents d'accés en 5 minuts des d'una mateixa IP és moltíssim per a una persona i poc per a un
atac de farciment de credencials. S'escull Captcha i no Block deliberadament: una oficina
amb NAT compartida pot superar el llindar legítimament, i un CAPTCHA la deixa passar mentre que un
bloqueig li impediria comprar.
Protecció de /api/pedidos, on sí que bloquegem:
{
"Name": "LimiteApiPedidos",
"Priority": 50,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"EvaluationWindowSec": 300,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/api/pedidos",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "LimiteApiPedidos"
}
}Com es calcula un llindar, amb les dades de MercadoFresco: el pic del divendres són 900 comandes per
hora, és a dir 15 per minut o 75 a la finestra de 5 minuts repartides entre tots els clients. Un
sol client que fes 2.000 peticions a /api/pedidos en 5 minuts no és un client. El llindar té un
marge de més de 25 vegades sobre el trànsit total del lloc: és conservador expressament, perquè un
llindar massa ajustat bloqueja abans el client rar que l'atacant.
La Web ACL de MercadoFresco, completa
Aquest és el fitxer de la Web ACL de CloudFront, en la fase 1 del desplegament: tot en Count.
{
"Name": "waf-mercadofresco-cdn",
"Scope": "CLOUDFRONT",
"DefaultAction": { "Allow": {} },
"Description": "Proteccio de la botiga de MercadoFresco - fase d'observacio",
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "wafMercadofrescoCdn"
},
"Rules": [
{
"Name": "BloqueoManual",
"Priority": 0,
"Statement": {
"IPSetReferenceStatement": {
"ARN": "arn:aws:wafv2:us-east-1:111122223333:global/ipset/ipset-mercadofresco-bloqueo/abc123"
}
},
"Action": { "Block": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "BloqueoManual"
}
},
{
"Name": "ReputacionIp",
"Priority": 10,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesAmazonIpReputationList"
}
},
"OverrideAction": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "ReputacionIp"
}
},
{
"Name": "ConjuntoComun",
"Priority": 20,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "ConjuntoComun"
}
},
{
"Name": "EntradasMaliciosas",
"Priority": 25,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"OverrideAction": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "EntradasMaliciosas"
}
},
{
"Name": "InyeccionSql",
"Priority": 30,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesSQLiRuleSet"
}
},
"OverrideAction": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "InyeccionSql"
}
},
{
"Name": "LimiteLogin",
"Priority": 40,
"Statement": {
"RateBasedStatement": {
"Limit": 100,
"EvaluationWindowSec": 300,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/login",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "LimiteLogin"
}
},
{
"Name": "LimiteApiPedidos",
"Priority": 50,
"Statement": {
"RateBasedStatement": {
"Limit": 2000,
"EvaluationWindowSec": 300,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/api/pedidos",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "LimiteApiPedidos"
}
}
]
}Observacions sobre aquest document:
- La regla de prioritat 0 sí que està en
Block, i és l'única. És l'IP set de bloqueig manual: el va omplint la Marta a mà durant un incident, així que no hi ha risc de fals positiu. - Totes les altres estan en
Count. Els grups gestionats amb"OverrideAction": {"Count": {}}i les regles pròpies amb"Action": {"Count": {}}. Són camps diferents per al mateix efecte, i confondre'ls és un error habitual. SampledRequestsEnabled: truea totes. Sense això no es poden veure les mostres de peticions a la consola, que és justament el que cal a la fase d'anàlisi.- Capacitat total: 1 (IP set) + 25 + 700 + 200 + 200 + 2 + 2 = 1.130 WCU de les 1.500 disponibles.
Queda marge per a unes poques regles més, però no per a
LinuxRuleSet(200) iAnonymousIpList(50) alhora sense demanar ampliació.
Creació per CLI:
# 1. L'IP set de bloqueig manual (buit al principi)
aws wafv2 create-ip-set \
--name ipset-mercadofresco-bloqueo \
--scope CLOUDFRONT --region us-east-1 \
--ip-address-version IPV4 --addresses \
--description "Bloqueig manual durant incidents" \
--tags Key=Proyecto,Value=mercadofresco Key=Componente,Value=waf \
--profile mercadofresco-dev
# 2. La Web ACL
aws wafv2 create-web-acl \
--cli-input-json file:///tmp/waf-mercadofresco-cdn.json \
--region us-east-1 \
--tags Key=Proyecto,Value=mercadofresco Key=Entorno,Value=produccion \
Key=Componente,Value=waf Key=Propietario,Value=marta \
Key=CentroCoste,Value=tecnologia \
--profile mercadofresco-devAfegir una IP al conjunt de bloqueig durant un incident requereix el LockToken, que actua com a
control de concurrència optimista:
TOKEN=$(aws wafv2 get-ip-set --name ipset-mercadofresco-bloqueo \
--scope CLOUDFRONT --id <id> --region us-east-1 \
--query 'LockToken' --output text --profile mercadofresco-dev)
aws wafv2 update-ip-set --name ipset-mercadofresco-bloqueo \
--scope CLOUDFRONT --id <id> --region us-east-1 \
--addresses 203.0.113.45/32 198.51.100.0/24 \
--lock-token "$TOKEN" --profile mercadofresco-devCompte: update-ip-set reemplaça la llista sencera, no hi afegeix. Cal llegir la llista
actual, afegir la nova adreça i enviar el conjunt complet. És un error clàssic que esborra
silenciosament bloquejos anteriors.
Associar la Web ACL a CloudFront i a l'ALB
CloudFront: s'associa actualitzant la configuració de la distribució amb l'ARN de la Web ACL. El desplegament triga uns minuts a propagar-se a tots els punts de presència.
ALB: s'associa directament i l'efecte és immediat.
aws wafv2 associate-web-acl \
--web-acl-arn arn:aws:wafv2:eu-west-1:111122223333:regional/webacl/waf-mercadofresco-alb/abc123 \
--resource-arn arn:aws:elasticloadbalancing:eu-west-1:111122223333:loadbalancer/app/alb-mercadofresco-tienda/50dc6c495c0c9188 \
--region eu-west-1 --profile mercadofresco-dev
# Comprovar quins recursos protegeix una Web ACL
aws wafv2 list-resources-for-web-acl \
--web-acl-arn arn:aws:wafv2:eu-west-1:111122223333:regional/webacl/waf-mercadofresco-alb/abc123 \
--region eu-west-1 --profile mercadofresco-devRecorda el detall de les regles per taxa a la Web ACL de l'ALB: com que tot el trànsit arriba des
de CloudFront, cal fer servir AggregateKeyType: FORWARDED_IP amb la capçalera X-Forwarded-For, o
el recompte no distingirà clients.
Registres de WAF i la seva anàlisi
Sense registres, WAF és una caixa negra i la fase 3 del desplegament és impossible. Hi ha tres destins:
| Destí | Latència | Cost | Quan |
|---|---|---|---|
| CloudWatch Logs | Segons | Més gran per GB | Anàlisi immediata, alarmes |
| S3 | Minuts | El més barat | Retenció llarga, anàlisi amb Athena |
| Kinesis Data Firehose | Segons | Mitjà | Enviar a un SIEM extern |
MercadoFresco fa servir CloudWatch Logs durant les dues setmanes d'observació —perquè necessita
consultar de seguida— i després manté l'enviament a mercadofresco-registros-web per a l'històric.
# El grup s'ha d'anomenar obligatoriament aws-waf-logs-*
aws logs create-log-group --log-group-name aws-waf-logs-mercadofresco \
--region us-east-1 --profile mercadofresco-dev
aws logs put-retention-policy --log-group-name aws-waf-logs-mercadofresco \
--retention-in-days 30 --region us-east-1 --profile mercadofresco-dev
aws wafv2 put-logging-configuration \
--logging-configuration '{
"ResourceArn": "arn:aws:wafv2:us-east-1:111122223333:global/webacl/waf-mercadofresco-cdn/abc123",
"LogDestinationConfigs": ["arn:aws:logs:us-east-1:111122223333:log-group:aws-waf-logs-mercadofresco"],
"RedactedFields": [
{"SingleHeader": {"Name": "authorization"}},
{"SingleHeader": {"Name": "cookie"}},
{"SingleQueryArgument": {"Name": "password"}}
]
}' \
--region us-east-1 --profile mercadofresco-devEl nom del grup ha de començar per aws-waf-logs- o la configuració es rebutja sense explicar per
què. I RedactedFields no és opcional en un entorn amb dades de clients: sense això, les galetes de
sessió i les capçaleres d'autorització acabarien en text pla als registres, creant exactament el
problema que vam resoldre a 04-03. És un punt de compliment del RGPD.
Consultes de la fase 3 amb CloudWatch Logs Insights:
-- Quines regles estan comptant i quantes vegades?
fields @timestamp, terminatingRuleId, action, httpRequest.uri
| filter action = "COUNT" or terminatingRuleId != "Default_Action"
| stats count(*) as coincidencies by terminatingRuleId
| sort coincidencies desc-- Detall de les peticions que una regla concreta hauria bloquejat
fields @timestamp, httpRequest.clientIp, httpRequest.uri, httpRequest.country,
httpRequest.headers.0.value
| filter @message like /SizeRestrictions_BODY/
| sort @timestamp desc
| limit 100-- Les 20 IP amb mes coincidencies
fields httpRequest.clientIp
| filter action = "COUNT"
| stats count(*) as intents by httpRequest.clientIp
| sort intents desc
| limit 20Aquesta primera consulta és la que decideix el desplegament: si SQLiRuleSet compta 4.000 vegades al
dia i totes les peticions són intents evidents d'injecció, passa a Block sense dubtar. Si
CommonRuleSet compta 300 vegades i 280 són peticions legítimes del teu propi formulari de comanda,
tens un fals positiu per resoldre abans.
Mètriques i mostres de peticions bloquejades
WAF publica a l'espai de noms AWS/WAFV2:
| Mètrica | Què mesura |
|---|---|
AllowedRequests |
Peticions permeses |
BlockedRequests |
Peticions bloquejades |
CountedRequests |
Coincidències en mode Count |
CaptchaRequests |
Reptes CAPTCHA servits |
PassedRequests |
Que van passar un repte |
aws cloudwatch get-metric-statistics \
--namespace AWS/WAFV2 --metric-name BlockedRequests \
--dimensions Name=WebACL,Value=waf-mercadofresco-cdn Name=Rule,Value=ALL Name=Region,Value=CloudFront \
--start-time 2026-08-01T00:00:00Z --end-time 2026-08-02T00:00:00Z \
--period 3600 --statistics Sum \
--region us-east-1 --profile mercadofresco-devI una alarma que avisa quan alguna cosa canvia bruscament, cap al mateix tema alertas-mercadofresco
de 04-04:
aws cloudwatch put-metric-alarm \
--alarm-name mercadofresco-waf-bloqueos-anomalos \
--alarm-description "Pic de bloquejos al WAF: possible atac o fals positiu nou" \
--namespace AWS/WAFV2 --metric-name BlockedRequests \
--dimensions Name=WebACL,Value=waf-mercadofresco-cdn Name=Rule,Value=ALL Name=Region,Value=CloudFront \
--statistic Sum --period 300 --evaluation-periods 2 \
--threshold 5000 --comparison-operator GreaterThanThreshold \
--alarm-actions arn:aws:sns:eu-west-1:111122223333:alertas-mercadofresco \
--region us-east-1 --profile mercadofresco-devFixa't que un pic de bloquejos té dues lectures possibles: t'estan atacant, o acabes d'introduir un fals positiu. Totes dues requereixen mirar, i per això l'alarma és útil en els dos casos.
A més, la consola ofereix les mostres de peticions: fins a 100 peticions de les últimes 3 hores
que van coincidir amb cada regla, amb les seves capçaleres completes. És l'eina més ràpida per
diagnosticar un fals positiu, i només funciona si SampledRequestsEnabled està a true.
aws wafv2 get-sampled-requests \
--web-acl-arn arn:aws:wafv2:us-east-1:111122223333:global/webacl/waf-mercadofresco-cdn/abc123 \
--rule-metric-name ConjuntoComun --scope CLOUDFRONT \
--time-window StartTime=2026-08-02T08:00:00Z,EndTime=2026-08-02T10:00:00Z \
--max-items 100 --region us-east-1 --profile mercadofresco-devFalsos positius: diagnòstic i exclusions
Un fals positiu és una petició legítima que una regla bloqueja. Els quatre casos que veuràs:
| Símptoma | Regla habitualment responsable | Causa |
|---|---|---|
| No es poden pujar fotos grans de producte | SizeRestrictions_BODY |
El cos supera el límit per defecte |
| Un producte amb cometes o apòstrofs al nom falla | SQLi_QUERYARGUMENTS |
L'apòstrof sembla injecció |
| Una integració interna deixa de funcionar | NoUserAgent_HEADER |
El client no envia agent d'usuari |
| Clients amb VPN corporativa no poden entrar | AnonymousIpList |
La seva sortida està catalogada com a anònima |
Procediment de diagnòstic:
- Identificar la regla exacta. El camp
terminatingRuleIddels registres, o la columna de la consola, dona el nom concret dins del grup. - Veure la petició completa a les mostres: URI, capçaleres, mida.
- Decidir l'abast mínim de l'excepció. No desactivis mai el grup sencer.
- Aplicar i verificar.
Tres maneres de resoldre-ho, de menys a més àmplia:
a) Passar només aquesta regla a Count:
b) Excloure aquesta regla només en una ruta concreta, combinant amb ScopeDownStatement. És
l'opció correcta: la regla continua protegint la resta del lloc.
{
"Name": "ConjuntoComunSalvoSubidas",
"Priority": 20,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"ScopeDownStatement": {
"NotStatement": {
"Statement": {
"ByteMatchStatement": {
"SearchString": "/admin/productos/subir",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
}
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "ConjuntoComunSalvoSubidas"
}
}c) Permetre explícitament abans, amb una regla de prioritat menor i acció Allow. És la més
perillosa: Allow és terminal, així que aquesta petició se salta totes les regles següents,
incloses les d'injecció SQL. Fes-la servir només per a trànsit plenament fiable, com el de la IP de
l'oficina.
I la regla d'or: documenta cada excepció. Una Web ACL amb quinze exclusions que ningú no recorda
per què hi són és una Web ACL que no protegeix res. Cada RuleActionOverride hauria de tenir associat
un comentari al repositori amb la data, el motiu i una data de revisió.
Cost i càlcul per a MercadoFresco
| Concepte | Preu |
|---|---|
| Web ACL | 5,00 USD al mes |
| Cada regla o grup de regles | 1,00 USD al mes |
| Peticions | 0,60 USD per milió |
| Grups gestionats gratuïts | 0 USD (només compten com a regla) |
| Bot Control | 10 USD/mes + 1 USD per milió analitzat |
| ATP | 10 USD/mes + 1 USD per cada 1.000 intents d'accés |
| Registres a CloudWatch Logs / S3 | Cost del servei de destí |
Càlcul per a MercadoFresco. Punt clau: la major part del trànsit se serveix des de la memòria cau de CloudFront, i aquestes peticions sí que passen per WAF. Comptem 4 milions de peticions al mes a la distribució i 400.000 que arriben a l'ALB:
| Concepte | Quantitat | Cost mensual |
|---|---|---|
| Web ACL de CloudFront | 1 | 5,00 USD |
| Regles a la Web ACL de CloudFront | 7 | 7,00 USD |
| Peticions (CloudFront) | 4.000.000 | 2,40 USD |
| Web ACL de l'ALB | 1 | 5,00 USD |
| Regles a la Web ACL de l'ALB | 5 | 5,00 USD |
| Peticions (ALB) | 400.000 | 0,24 USD |
| Registres a CloudWatch Logs | ~3 GB | ~1,50 USD |
| Total | 26,14 USD/mes |
Val la pena posar aquesta xifra en context:
| Opció | Cost mensual | Què cobreix |
|---|---|---|
| Shield Standard | 0 USD | Volumètrics L3/L4 |
| WAF amb aquesta configuració | 26 USD | Injecció SQL, XSS, bots, inundació L7 |
| Shield Advanced | 3.000 USD | L'anterior més automatització i crèdits |
26 dòlars al mes cobreixen l'amenaça més probable per a MercadoFresco. És la decisió de seguretat amb millor relació cost-benefici del mòdul després de KMS, i confirma l'avaluació de 04-04: els diners estaven molt més ben invertits aquí que en Shield Advanced.
Si el pressupost estrenyés, es pot retallar: fer servir una sola Web ACL a CloudFront i prescindir de la de l'ALB estalvia 10,24 USD al mes a canvi de perdre la defensa en profunditat. És un compromís defensable si l'ALB està ben tancat amb la capçalera verificada de 04-04.
Neteja
L'ordre importa: no es pot eliminar una Web ACL associada a un recurs.
# 1. Desassociar de cada recurs
aws wafv2 disassociate-web-acl \
--resource-arn arn:aws:elasticloadbalancing:eu-west-1:111122223333:loadbalancer/app/alb-mercadofresco-tienda/50dc6c495c0c9188 \
--region eu-west-1 --profile mercadofresco-dev
# A CloudFront: treure el WebACLId de la configuracio de la distribucio
# i esperar que acabi el desplegament
# 2. Treure la configuracio de registres
aws wafv2 delete-logging-configuration \
--resource-arn arn:aws:wafv2:us-east-1:111122223333:global/webacl/waf-mercadofresco-cdn/abc123 \
--region us-east-1 --profile mercadofresco-dev
# 3. Eliminar la Web ACL (necessita el LockToken)
TOKEN=$(aws wafv2 get-web-acl --name waf-mercadofresco-cdn --scope CLOUDFRONT \
--id abc123 --region us-east-1 --query 'LockToken' --output text --profile mercadofresco-dev)
aws wafv2 delete-web-acl --name waf-mercadofresco-cdn --scope CLOUDFRONT \
--id abc123 --lock-token "$TOKEN" --region us-east-1 --profile mercadofresco-dev
# 4. Eliminar l'IP set i el grup de registres
aws wafv2 delete-ip-set --name ipset-mercadofresco-bloqueo --scope CLOUDFRONT \
--id <id> --lock-token <token> --region us-east-1 --profile mercadofresco-dev
aws logs delete-log-group --log-group-name aws-waf-logs-mercadofresco \
--region us-east-1 --profile mercadofresco-devSi deixes la Web ACL creada però sense associar, continua costant 5 USD al mes més 1 USD per regla. És un dels càrrecs fantasma més comuns a les factures d'AWS.
La postura de seguretat de MercadoFresco
Amb aquesta lliçó es tanca el mòdul. Aquesta és l'arquitectura de seguretat completa:
flowchart TD
A["Client a internet"] --> B["Route 53 mercadofresco.example<br/>Shield Standard"]
B --> C["CloudFront E2QWERTY123ABC<br/>Shield Standard + memoria cau + TLS d'ACM"]
C --> D["AWS WAF waf-mercadofresco-cdn<br/>Regles gestionades + per taxa"]
D --> E["ALB alb-mercadofresco-tienda<br/>Capcalera verificada + prefixos CloudFront<br/>waf-mercadofresco-alb"]
E --> F["ASG asg-mercadofresco-tienda<br/>Subxarxes privades app-a/-b<br/>rol-mercadofresco-tienda: minim privilegi"]
F --> G["Secrets Manager<br/>mercadofresco/produccion/rds/mfadmin<br/>rotacio cada 30 dies"]
F --> H["Parameter Store<br/>/mercadofresco/produccion/*"]
G --> I["RDS mercadofresco-pedidos<br/>Multi-AZ, xifrada amb KMS<br/>Subxarxes datos-a/-b sense sortida"]
F --> I
C -.->|"OAC oac-mercadofresco-catalogo"| J["S3 mercadofresco-catalogo-fotos<br/>SSE-KMS + clau de bucket"]
K["KMS alias/mercadofresco-datos<br/>Marta administra, els rols usen<br/>rotacio anual"] -.-> I
K -.-> J
K -.-> G
Les quatre capes, i el que aporta cadascuna:
| Capa | Serveis | Què garanteix |
|---|---|---|
| Identitat | IAM, rols, grups, MFA | Ningú no té més permisos dels que necessita; res no porta claus permanents |
| Dades | KMS, SSE-KMS, EBS i RDS xifrats | Un disc, una instantània o un bucket exposat no revelen res |
| Secrets | Secrets Manager, Parameter Store | Cap credencial viu en un fitxer; roten soles cada 30 dies |
| Vora | Shield Standard, WAF, OAC, SG | El trànsit maliciós es descarta lluny i la superfície exposada és mínima |
I el cost total del mòdul 4:
| Servei | Cost mensual |
|---|---|
| IAM, Identity Center, Access Analyzer extern | 0,00 USD |
| KMS (1 clau + peticions) | 1,00 USD |
| Secrets Manager (2 secrets) + Parameter Store | 0,82 USD |
| Shield Standard | 0,00 USD |
| WAF (2 Web ACL, 12 regles, 4,4 M peticions) | 26,14 USD |
| Alarmes de CloudWatch | 0,40 USD |
| Total | 28,36 USD/mes |
Menys de 30 dòlars al mes per identitats mínimes, dades xifrades, secrets rotats i una vora protegida. Comparat amb els 3.000 de Shield Advanced —o amb el cost d'una bretxa notificable sota el RGPD— és la millor decisió d'inversió de tot el curs.
Errors Habituals i Consells
Crear la Web ACL de CloudFront a la regió equivocada. Ha de ser us-east-1 amb
--scope CLOUDFRONT. Una Web ACL regional no es pot associar a una distribució, i l'error no ho diu
de manera evident.
Desplegar directament en Block. L'error més car d'aquesta disciplina. Dues setmanes en Count,
anàlisi dels registres, exclusions i només llavors Block, d'una regla en una.
Oblidar les transformacions de text. Una regla que busca <script> sense LOWERCASE ni
URL_DECODE s'esquiva amb %3CSCRIPT%3E. Costen 10 WCU cadascuna i són el que fa que la regla
serveixi per a alguna cosa.
Confondre OverrideAction amb Action. Els grups de regles fan servir OverrideAction; les
regles pròpies fan servir Action. Posar el camp equivocat fa que l'API ho rebutgi o, pitjor, que el
mode Count que creies haver posat no hi sigui.
Fer servir AggregateKeyType: IP al WAF de l'ALB darrere de CloudFront. Totes les peticions
semblaran venir de les IP de CloudFront i la regla per taxa comptarà tot el trànsit del lloc com una
sola font. Allà cal fer servir FORWARDED_IP.
Esgotar les WCU sense adonar-se'n. CommonRuleSet consumeix 700 de 1.500. Planifica el
pressupost abans d'afegir grups, i consulta la capacitat amb describe-managed-rule-group.
Reemplaçar l'IP set en lloc d'ampliar-lo. update-ip-set substitueix la llista sencera. Llegeix
primer, afegeix i envia el conjunt complet.
Posar una regla Allow àmplia al capdamunt. Allow és terminal: aquesta petició se salta totes
les regles d'injecció SQL i de taxa que vinguin després. Reserva l'Allow explícit per a trànsit
plenament fiable.
No configurar RedactedFields als registres. Les galetes de sessió i les capçaleres
d'autorització acabarien en text pla, desfent la feina de 04-03 i creant un problema de RGPD.
Deixar una Web ACL creada i sense associar. Continua costant 5 USD al mes més 1 USD per regla.
Consell: versiona la Web ACL a Git. El JSON complet al repositori, revisat per una altra persona abans d'aplicar-se, amb un comentari per cada exclusió explicant data, motiu i revisió prevista. A 09-01 i 09-02 passarà a ser una plantilla de CloudFormation o un constructe de CDK.
Consell: revisa les exclusions cada trimestre. S'acumulen, i cadascuna és un forat que algú va obrir per una raó que probablement ja no existeix.
Consell: prefereix Challenge a CAPTCHA. Filtra bots simples sense fricció per al client.
Guarda el CAPTCHA per a les rutes realment crítiques.
Consell: tingues preparada per endavant la regla d'emergència. Un JSON a punt amb una regla per taxa agressiva sobre tot el lloc, que la Marta pugui aplicar en un minut durant un incident, estalvia mitja hora d'escriure sota pressió.
Exercicis
Exercici 1: dissenyar la Web ACL d'admin.mercadofresco.example
El subdomini d'administració que vam crear a 03-05 només ha de ser accessible des de l'oficina
(192.168.10.0/24 interna, amb sortida pública 203.0.113.10/32) i des del teletreball de la Marta,
amb IP dinàmica espanyola. Requisits: ningú més no hi ha d'arribar; els intents d'accés des d'altres
països ni tan sols han de tocar l'aplicació; cal registrar tots els intents rebutjats; i la solució
no pot impedir que la Marta treballi des de casa quan li canviï la IP.
Dissenya la Web ACL: model d'acció per defecte, llista de regles amb prioritats i accions, i justifica el compromís entre seguretat i usabilitat de l'última condició.
Exercici 2: analitzar la fase d'observació i decidir el pas a Block
Després de 14 dies amb waf-mercadofresco-cdn en Count, els registres donen aquests resultats:
| Regla | Coincidències | Mostra de peticions |
|---|---|---|
AmazonIpReputationList |
12.400 | Escanejos a /wp-login.php, /.env, /admin.php |
SQLiRuleSet |
3.100 | 3.050 amb ' OR '1'='1; 50 són cerques de "L'Escala" |
KnownBadInputsRuleSet |
890 | Totes amb cadenes de Log4Shell |
CommonRuleSet / SizeRestrictions_BODY |
420 | 415 són pujades de fotos des de /admin/productos/subir |
CommonRuleSet / NoUserAgent_HEADER |
310 | 295 del monitor intern de la Marta; 15 d'escàners |
CommonRuleSet / CrossSiteScripting_BODY |
45 | Totes malicioses |
LimiteLogin |
8 | 6 d'una IP amb 400 intents; 2 de l'oficina en hora punta |
LimiteApiPedidos |
0 | — |
Per a cada regla, decideix: passar a Block, mantenir en Count, o aplicar una exclusió concreta.
Per a les que requereixin exclusió, escriu el JSON. Justifica cada decisió i digues en quin ordre
aplicaries els canvis.
Exercici 3: escriure la resposta a un incident en calent
Un divendres a les 18:40, en ple pic, MercadoFresco rep un atac de capa 7: 30.000 peticions per minut
a /buscar?q=<aleatori> des de 5.000 IP de 40 països, amb agents d'usuari variats i realistes. La
taxa d'encerts de memòria cau s'ha enfonsat al 8 % i DatabaseConnections és a 185 de 200. La Web
ACL està desplegada i en Block per a les regles gestionades.
Escriu la seqüència exacta d'accions de la Marta durant els primers 15 minuts, incloent-hi el JSON de les regles que aplicaria, el risc de cada acció sobre els clients legítims que estan comprant en aquell moment, i què comprovaria després de cada pas. Tingues en compte que és divendres a les 18:40: és el pitjor moment possible per bloquejar trànsit real.
Solucions
Solució 1
Model: acció per defecte Block (llista blanca). És un tauler d'administració, no un lloc
públic: el correcte és negar-ho tot i permetre l'explícit.
| Prioritat | Regla | Declaració | Acció |
|---|---|---|---|
| 0 | OficinaPermitida |
IPSetReferenceStatement → ipset-mercadofresco-oficina (203.0.113.10/32) |
Allow |
| 10 | SoloEspana |
NotStatement(GeoMatchStatement ES) |
Block |
| 20 | MartaConDesafio |
ByteMatchStatement sobre /admin |
Challenge |
| 30 | ProteccionesBase |
AWSManagedRulesCommonRuleSet + KnownBadInputs |
Block |
| — | Per defecte | — | Block |
Com funciona el recorregut: el trànsit de l'oficina coincideix a la prioritat 0 i es permet de
seguida, saltant-se tota la resta. El de fora d'Espanya es bloqueja a la 10. El que queda —trànsit
espanyol que no és de l'oficina, és a dir, potencialment la Marta des de casa— rep un Challenge
silenciós a la 20, que un navegador real resol sol i un script no. Tota la resta cau al Block per
defecte.
El compromís de l'última condició. L'opció màximament segura seria permetre només les IP de
l'IP set, però això obliga la Marta a trucar a algú cada vegada que el seu operador li canvia la IP,
cosa que acaba sempre amb algú afegint 0.0.0.0/0 «temporalment» un diumenge. La solució proposada
accepta un risc controlat: qualsevol amb IP espanyola que arribi a /admin supera el Challenge si
fa servir un navegador. Però això no li dona accés: continua havent-hi autenticació amb MFA al
darrere, i WAF és només la primera barrera. El que s'aconsegueix és eliminar el 99,9 % del soroll
automatitzat sense bloquejar la persona que administra el sistema.
L'alternativa millor, si es vol apujar el llistó sense perdre usabilitat, és posar el tauler darrere
d'una VPN o d'un client de Verified Access amb IP de sortida fixa, i tornar al model de llista blanca
estricta. Registre: la configuració de registres de la Web ACL captura tots els bloquejos, amb
RedactedFields sobre authorization i cookie.
Solució 2
| Regla | Decisió | Justificació |
|---|---|---|
AmazonIpReputationList |
Block |
12.400 coincidències, cap fals positiu: escanejos purs contra rutes que MercadoFresco ni tan sols té |
SQLiRuleSet |
Block amb exclusió |
El 98 % són atacs reals, però 50 són cerques legítimes de topònims amb apòstrof |
KnownBadInputsRuleSet |
Block |
890 intents de Log4Shell, cap fals positiu |
SizeRestrictions_BODY |
Exclusió de ruta | 415 de 420 són pujades legítimes de fotos de producte |
NoUserAgent_HEADER |
Exclusió o arreglar el monitor | 295 de 310 són el monitor intern |
CrossSiteScripting_BODY |
Block |
45 coincidències, totes malicioses |
LimiteLogin |
Block... no: Captcha |
6 de 8 són un atac clar, però 2 són l'oficina |
LimiteApiPedidos |
Block |
Cap coincidència en 14 dies: el llindar de 2.000 és folgat i no hi ha risc |
Exclusió per a SizeRestrictions_BODY (opció b, la correcta: la regla continua activa a la resta
del lloc):
{
"Name": "ConjuntoComun",
"Priority": 20,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet",
"ScopeDownStatement": {
"NotStatement": {
"Statement": {
"ByteMatchStatement": {
"SearchString": "/admin/productos/subir",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"RuleActionOverrides": [
{ "Name": "NoUserAgent_HEADER", "ActionToUse": { "Count": {} } }
]
}
},
"OverrideAction": { "None": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "ConjuntoComun"
}
}Per a SQLiRuleSet, les 50 cerques de "L'Escala" no justifiquen desactivar la protecció contra
injecció SQL en una botiga amb PostgreSQL al darrere. Hi ha dues sortides i la bona no és a WAF:
- Correcta: arreglar el frontal perquè codifiqui correctament l'apòstrof abans d'enviar-lo, o fer servir POST amb JSON en lloc de la cadena de consulta. El fals positiu desapareix sol.
- Acceptable com a pedaç temporal: aplicar
RuleActionOverridesnomés sobreSQLi_QUERYARGUMENTSi únicament a la ruta/buscar, deixant actiu la resta del grup. Amb data de revisió anotada.
Per a NoUserAgent_HEADER, la solució de fons no és una excepció a WAF sinó arreglar el
monitor perquè enviï un agent d'usuari identificatiu (MercadoFresco-Monitor/1.0). Mentrestant,
l'excepció de dalt el manté en Count.
Per a LimiteLogin, Captcha en lloc de Block: les 2 coincidències de l'oficina són una NAT
compartida en hora punta, i un bloqueig impediria treballar a tot l'equip. El CAPTCHA deixa passar
les persones i atura l'atac de 400 intents.
Ordre d'aplicació, seguint la taula de risc:
AmazonIpReputationList→Block. Vigilar 24 h.KnownBadInputsRuleSet→Block. Vigilar 24 h.LimiteApiPedidos→BlockiLimiteLogin→Captcha. Vigilar 24 h.- Arreglar el monitor i el frontal de cerca (canvis d'aplicació, no de WAF).
SQLiRuleSet→Block, amb l'exclusió de/buscarsi el frontal encara no està arreglat.CommonRuleSet→Blockamb l'exclusió de ruta per a les pujades. L'últim i el més vigilat: és el grup més ampli.
I cap d'aquests canvis s'aplica un divendres.
Solució 3
Context que condiciona tot: divendres 18:40, pic de 900 comandes/hora, clients reals comprant. Cada minut de bloqueig indiscriminat són comandes perdudes. La prioritat és mantenir el servei, no guanyar el combat.
Minuts 0-2: confirmar i caracteritzar. No es toca res encara.
Es miren les tres mètriques que decideixen: PedidosPorHora de l'espai MercadoFresco/Tienda (si
continua a ~900, hi ha clients reals comprant ara mateix), CacheHitRate (8 %, confirmat) i
DatabaseConnections (185/200, crític). Amb agents d'usuari realistes i 5.000 IP repartides, el
patró identificable no és l'origen sinó la ruta.
Minuts 2-4: alleujar la base de dades, que és el que caurà. És l'acció de menor risc i major efecte, i no bloqueja ningú:
- Augmentar el TTL mínim de les respostes de
/buscara CloudFront a 60 segons. - Reduir la clau de memòria cau de
/buscarperquè només incloguiqnormalitzat, ignorant els altres paràmetres aleatoris. Això converteix part de l'atac en encerts de memòria cau.
Risc per a clients legítims: resultats de cerca fins a un minut desactualitzats. Irrellevant.
Comprovar després: CacheHitRate i DatabaseConnections en els 3 minuts següents.
Minuts 4-7: regla per taxa sobre /buscar, primer en Count.
{
"Name": "EmergenciaBuscar",
"Priority": 45,
"Statement": {
"RateBasedStatement": {
"Limit": 300,
"EvaluationWindowSec": 60,
"AggregateKeyType": "IP",
"ScopeDownStatement": {
"ByteMatchStatement": {
"SearchString": "/buscar",
"FieldToMatch": { "UriPath": {} },
"TextTransformations": [{ "Priority": 0, "Type": "LOWERCASE" }],
"PositionalConstraint": "STARTS_WITH"
}
}
}
},
"Action": { "Count": {} },
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "EmergenciaBuscar"
}
}Risc: cap, Count no bloqueja. Comprovar: CountedRequests de la regla i les mostres. Si
les peticions comptades són les de l'atac i no clients normals, es passa al pas següent. Sí,
es compten dos minuts fins i tot en una emergència: és exactament el moment en què un error costa
més car.
Minuts 7-9: passar a Challenge, no a Block.
Es canvia "Action": { "Count": {} } per "Action": { "Challenge": {} }. Un navegador real resol el
repte de JavaScript de manera invisible i continua comprant; un bot que no executa JavaScript no
passa.
Risc: mínim. Només afectaria clients amb JavaScript desactivat, pràcticament inexistents en una
botiga que ja el requereix. Comprovar: PassedRequests enfront de BlockedRequests,
DatabaseConnections i, sobretot, PedidosPorHora: si continua a 900, no estem fent mal al
negoci.
Minuts 9-12: si l'atac persisteix, endurir només amb els pitjors.
Si DatabaseConnections continua per damunt de 180, s'afegeix una segona regla per taxa amb llindar
molt alt (per exemple 1.000 per minut per IP sobre /buscar) en Block. Aquest llindar és impossible
d'assolir per a una persona, així que el risc de fals positiu és gairebé nul.
Comprovar: que PedidosPorHora no cau.
Minuts 12-15: capacitat i comunicació.
- Derivar les lectures de cerca a la rèplica
mercadofresco-pedidos-lecturasi l'aplicació ho admet per configuració, per descarregar la instància principal. - Apujar temporalment el màxim de l'ASG.
- Avisar el Luis i la Sara de l'estat, i anotar l'hora de cada acció per al post-mortem.
El que la Marta NO fa, i és tan important com el que fa:
- No bloqueja per país. 40 països, i alguns tindran clients espanyols de viatge.
- No bloqueja les 5.000 IP. Tornaran amb unes altres i bloquejaria clients que comparteixen NAT.
- No passa
CommonRuleSeta mode agressiu ni toca regles que no ha provat: un fals positiu nou a les 18:40 d'un divendres és pitjor que l'atac. - No apaga la cerca sencera, que és una funcionalitat central d'una botiga d'alimentació.
- No desactiva CloudFront per «veure si és cosa seva»: eliminaria l'única capa que la protegeix.
- No aplica cap canvi permanent en calent. Les regles d'emergència es marquen com a temporals i es revisen el dilluns amb dades, no el divendres amb adrenalina.
Conclusió
MercadoFresco té per fi la capa que faltava. Saps què veu AWS WAF que ni un grup de seguretat ni
Shield poden veure —el mètode, la ruta, la cadena de consulta, les capçaleres, les galetes i el cos
de cada petició HTTP— i per què les tres eines són complementàries i cap no en substitueix una altra.
Coneixes l'anatomia d'una Web ACL: regles amb prioritat, grups gestionats, IP sets, el pressupost
de WCU que CommonRuleSet consumeix gairebé a la meitat, i les cinc accions —amb Allow i
Block terminals, Count mai terminal, i Challenge com l'opció que filtra bots sense cobrar
fricció al client.
Saps que la Web ACL de CloudFront es crea a us-east-1 amb --scope CLOUDFRONT, el mateix
parany que ACM i les mètriques de la CDN. Domines les declaracions de coincidència i, sobretot,
les transformacions de text: sense LOWERCASE ni URL_DECODE, una regla contra <script>
s'esquiva escrivint %3CSCRIPT%3E, i per això cada transformació costa 10 WCU ben gastades. Coneixes
els grups gestionats per AWS —CommonRuleSet, KnownBadInputs, SQLi, Linux,
AmazonIpReputationList, AnonymousIpList, i els de pagament BotControl i ATP— i que els quatre
primers són gratis i cobreixen la immensa majoria del que veuràs.
I per damunt de tot domines el mètode de desplegament, que és el que separa una Web ACL útil d'una
apagada autoinfligida: tot en Count, dues setmanes incloent-hi dos divendres, analitzar els
registres, excloure amb l'abast mínim, i només llavors passar a Block d'una regla en una, començant
per la que menys falsos positius produeix. Has muntat les regles per taxa que protegeixen /login
amb Captcha i /api/pedidos amb Block, sabent calcular el llindar a partir del trànsit real i
fer servir FORWARDED_IP quan el WAF és a l'ALB darrere de CloudFront. Tens els registres a
aws-waf-logs-mercadofresco amb RedactedFields sobre galetes i capçaleres d'autorització, les
consultes d'Insights que decideixen el pas a Block, les mètriques d'AWS/WAFV2 amb la seva alarma
cap a alertas-mercadofresco, i el procediment per diagnosticar i acotar un fals positiu sense
desactivar un grup sencer. Tot per 26,14 USD al mes, enfront dels 3.000 de Shield Advanced.
Amb això es tanca el mòdul 4. La postura de seguretat de MercadoFresco descansa sobre quatre
capes: identitats mínimes amb rols sense claus permanents, grups humans i MFA obligatori; dades
xifrades amb alias/mercadofresco-datos, separació de funcions i rotació anual; secrets
custodiats a mercadofresco/produccion/rds/mfadmin amb rotació automàtica cada 30 dies; i una
vora protegida per Shield Standard, dues Web ACL i una superfície exposada reduïda al mínim. Tot
plegat per 28,36 USD mensuals, i amb una decisió raonada i documentada de no contractar
Shield Advanced.
I tanmateix, hi ha alguna cosa profundament incompleta en tot això, i és el que dona nom al mòdul següent. Hem construït alarmes que envien avisos a un tema d'SNS, però ningú no ha comprovat mai que aquest avís arribi de debò a un telèfon a les quatre de la matinada. Els registres de WAF, de la VPC, de l'ALB i de les funcions Lambda s'estan acumulant en cinc llocs diferents sense que ningú els correlacioni. Quan un client escrigui dient que la seva comanda triga vuit segons a confirmar-se, la Marta no tindrà manera de saber si el problema és a la botiga, a la Lambda d'estat de comanda o a la base de dades. Ningú no sap qui va desxifrar l'última còpia de la base de dades, ni quan, encara que KMS ho hagi registrat escrupolosament. I no existeix cap mecanisme que avisi si demà algú desactiva el xifratge d'un bucket o obre un grup de seguretat al món.
Dit d'una altra manera: està tot muntat, i ningú no està mirant. Al mòdul 5, «Monitoratge i
gestió», començant per la lliçó 05-01 «Amazon CloudWatch», construirem aquesta mirada:
mètriques, taulers, registres centralitzats i alarmes que funcionen de debò; després el rastreig
d'una petició d'extrem a extrem amb X-Ray, l'auditoria de cada crida a l'API amb CloudTrail
—on per fi veurem qui va fer servir kms:Decrypt i qui va llegir quin secret—, la vigilància
contínua del compliment amb AWS Config, que avisa tan bon punt una configuració es desvia del que
acabem de construir, i les recomanacions automàtiques de Trusted Advisor.
Curs d'AWS
Mòdul 1: Introducció a AWS
- Què és AWS?
- Configuració del teu compte d'AWS
- Infraestructura global d'AWS
- Consola d'administració d'AWS
- AWS CLI i SDK
Mòdul 2: Serveis principals d'AWS
Mòdul 3: Xarxes i lliurament de contingut
- Amazon VPC
- Grups de seguretat i llistes de control d'accés
- Elastic Load Balancing
- Amazon CloudFront
- Route 53
Mòdul 4: Seguretat i identitat
- AWS Identity and Access Management (IAM)
- AWS Key Management Service (KMS)
- Secrets Manager i Parameter Store
- AWS Shield
- AWS WAF
Mòdul 5: Monitoratge i gestió
- Amazon CloudWatch
- AWS X-Ray i traçabilitat distribuïda
- AWS CloudTrail
- AWS Config
- AWS Trusted Advisor
Mòdul 6: Bases de dades
- Com triar la base de dades adequada
- Amazon DynamoDB
- Amazon Aurora
- Amazon Redshift
- Amazon ElastiCache
Mòdul 7: Integració d'aplicacions
- Amazon SQS
- Amazon SNS
- Amazon EventBridge
- AWS Step Functions
- Patrons d'integració: idempotència, reintents i cues de missatges fallits
