Edge protection for a small app, without the enterprise invoice

Security theatre around small apps usually goes like this. Someone reads about a breach, gets nervous, and the conversation jumps straight to a managed WAF product with a per-request price and a sales call attached. Meanwhile the app has no rate limiting, no bot filtering, and a public endpoint that will happily answer a SQL injection attempt all day.

The unglamorous truth is that the first eighty percent of edge protection for a small or mid-size app is nearly free, ships with AWS, and takes an afternoon. You do not need to buy anything exciting. You need to turn on the boring things and actually read what they log.

What you already have for free

Shield Standard is on, automatically, for everything behind CloudFront, an ALB, or Route 53. It absorbs the common volumetric and network-layer DDoS attacks without you configuring anything or paying anything. The expensive tier (Shield Advanced) exists for organisations with a genuine target on their back and a need for the response team and cost protections. Most small apps do not need it, and pretending otherwise is just money set on fire. Accept Standard, note it, move on.

AWS WAF is the piece you do configure, and it is priced so that a small app pays single-digit dollars a month. You attach a web ACL to your ALB (or CloudFront) and add rules. The trap is thinking you need to hand-write clever rules. You don’t. AWS maintains managed rule groups that cover the boring majority of what actually hits you.

The web ACL I reach for

Four things, in this order, and none of them are exotic:

# create the web ACL scoped to a regional resource (ALB)
aws wafv2 create-web-acl \
  --name my-app-waf \
  --scope REGIONAL \
  --default-action Allow={} \
  --visibility-config \
    SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=my-app-waf \
  --rules file://rules.json

Inside rules.json, priority-ordered:

  1. AWSManagedRulesCommonRuleSet: the broad baseline. Catches a large slice

of generic malicious patterns.

  1. AWSManagedRulesKnownBadInputsRuleSet: requests that are known-bad on

sight, like exploit signatures.

  1. AWSManagedRulesSQLiRuleSet: SQL injection patterns, because your app

talks to a database and the internet knows it.

  1. A rate-based rule: a hard ceiling on requests per IP over five minutes.

This is the one that quietly stops the dumb, high-volume nonsense: scrapers, credential-stuffing, someone’s misconfigured script.

{
  "Name": "rate-limit",
  "Priority": 10,
  "Statement": {
    "RateBasedStatement": { "Limit": 2000, "AggregateKeyType": "IP" }
  },
  "Action": { "Block": {} },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "rate-limit"
  }
}

Then attach it to the load balancer:

aws wafv2 associate-web-acl \
  --web-acl-arn arn:aws:wafv2:ap-southeast-2:111122223333:regional/webacl/my-app-waf/abc123 \
  --resource-arn arn:aws:elasticloadbalancing:ap-southeast-2:111122223333:loadbalancer/app/my-alb/abc123

The step that people forget: log it

A WAF that blocks silently is only half a control. If you can’t see what it caught, you can’t tune the rate limit, you can’t tell an attack from a legitimate traffic spike, and you can’t prove the thing works when someone asks. Send the logs somewhere with a retention you actually chose:

# a log group with a deliberate retention, then wire WAF to it
aws logs put-retention-policy --log-group-name aws-waf-logs-my-app --retention-in-days 90
aws wafv2 put-logging-configuration --logging-configuration \
  ResourceArn=<web-acl-arn>,LogDestinationConfigs=<log-group-arn>

Ninety days is a reasonable default for a small app: long enough to investigate an incident, short enough to keep storage trivial. The point isn’t the exact number. The point is that you picked it on purpose instead of letting logs pile up forever or, worse, not exist.

Start in count mode if you’re nervous

If you’re worried a managed rule will block real users, set the rule action to Count first. It logs what it would have blocked without blocking it. Watch for a week, confirm you’re not clipping legitimate traffic, then flip it to Block. This is the difference between a security control you trust and one you disable in a panic the first time someone complains they can’t log in.

The boring conclusion

Edge protection for a small app is not a purchasing decision, it’s a configuration you’ve been putting off. Shield Standard is already on. A WAF with three managed rule groups and a rate limit costs about as much as a coffee subscription and stops the overwhelming majority of what actually reaches a small app. Log it with a retention you chose, start in count mode if you’re cautious, and you’re done. Save the expensive products for when you can point at a real threat model that needs them, not a vague anxiety.


Comments

Leave a Reply

Your email address will not be published. Required fields are marked *