Control vs property · Layer conflation
A WAF is not an application security programme.
A WAF Is Not an Application Security Programme
Network security filters and segments traffic before it arrives, while application security controls the code paths, authorization logic, and input handling that decide what accepted traffic can do.
“The WAF is in blocking mode, so the application is protected from web attacks.”
Hold on.
That is about to become your reason for cancelling the code-review budget.
A WAF is not an application security programme.
A WAF can reject some HTTP requests before they reach your application.
That is a network-facing enforcement point.
Your application still decides what an accepted request means.
Which record it names.
Whether the caller owns it.
Whether a value becomes query data or query syntax.
Whether a byte stream is deserialized.
Whether a URL becomes an outbound request.
Those are application decisions.
A blocking rule cannot retrospectively install an authorization check in a handler, or make a deserializer safe.
A filter isn’t a time machine.
This should not be controversial.
Nobody says “the firewall permits only the payroll application, so the payroll application no longer needs permissions.”
Then the word WAF appears, an OWASP rule set gets a dashboard, and suddenly a request filter has acquired the job description of an engineering programme.
It has not.
Network security answers which traffic may reach a boundary.
Application security answers what the receiving code does with traffic that passed it.
NIST SP 800-41 Rev.
1 recommends deny-by-default for traffic not expressly permitted.
Exactly the right posture for a firewall.
It isn’t a verdict on the safety of the permitted application.
1. “But It Has Managed OWASP Rules.”
Good.
Managed rules can recognize and block some known request patterns.
Run them in monitored blocking mode where your operational evidence supports it.
But a managed rule set is still a rule set observing traffic.
An inline IPS at a chokepoint has the same basic constraint.
It can apply signature-based rules, anomaly-based baselines, or stateful protocol analysis.
Signature logic is useful for known threats and structurally weak for novel ones.
Anomaly logic has the inverse trade-off.
Protocol analysis understands an expected protocol sequence.
None of those mechanisms reads a product owner’s intent for an authenticated business action.
Consider an endpoint that accepts an ordinary record identifier and returns a record the caller should not read.
Your request can be well-formed.
The identifier can have the right shape.
The HTTP method can be expected.
The session can be valid.
The route can be exactly where your firewall rule says it belongs.
The missing question is semantic.
Does this record belong to this caller, for this operation?
A reviewer can see whether that check belongs in the changed endpoint.
A traffic rule can’t generally derive it from the wire.
OWASP says validation should happen as soon as data arrives from an external party, including partner, supplier and regulator feeds.
Already broader than “internet requests.”
More importantly, OWASP says input validation should not be your primary method of preventing injection.
A WAF rule is not improved by calling it managed, if the interpreter still receives a value it can treat unsafely.
Managed rules are a detection and blocking layer. Authorization intent is application logic.
2. “But It Blocks Obvious Injection Strings.”
It may.
Blocking an obvious pattern beats letting it through untouched.
That remains nowhere near the same thing as making your data access safe.
A denylist defines forbidden strings, characters or patterns.
OWASP calls that a common mistake, because such filters are trivial to bypass and because they block legitimate values the author did not anticipate.
The corpus example is the surname O’Brian.
A filter can be both too narrow for the dangerous cases and too broad for your customer.
Your application-layer control is parameterization.
A parameterized query or prepared statement prevents a substituted value from altering the query’s structure, regardless of the value’s content.
That isn’t simply a more elaborate regex.
It moves the protection to your database driver’s interpretation boundary.
The value stays data, rather than getting an opportunity to become part of the command structure.
The distinction isn’t cosmetic.
SAST can trace a request parameter, file read or deserialization entry point, a source, to query execution, process spawning or a URL fetch call, a sink.
It flags a source-to-sink path that reaches the sink without a recognized sanitizer, against source, bytecode or a build artifact, without running the application.
Your WAF sees your request arriving.
SAST sees the path from that request into the dangerous operation.
Your database driver enforces the meaning of a parameter at the query boundary.
Three different positions in your system.
Pretending the first subsumes the other two is how a filter becomes an excuse for leaving the actual fix unfunded.
And no, a more ambitious string rule doesn’t change the category.
Writing a longer list of bad text still asks the filter author to predict every future bad text.
The security equivalent of declaring that a sieve will catch water once the holes are labelled more confidently.
3. “But the Application Is Not Publicly Reachable.”
Then your network control has reduced reachability.
Use it.
That is what it is for.
NIST SP 800-41 Rev.
1 says traffic not expressly permitted by policy should be blocked.
PCI DSS v4.0 is more specific for a cardholder data environment.
Requirements 1.3.1 and 1.3.2 restrict inbound and outbound traffic to what is necessary and specifically deny everything else.
Requirement 1.4.4 says systems storing cardholder data are not directly accessible from untrusted networks.
A useful boundary with a useful name.
It is still a boundary around a permitted path.
Not a guarantee about behaviour after arrival.
Your application can receive traffic from a permitted service.
An authenticated user.
A queue consumer.
A partner feed.
An internal network segment.
Each is an untrusted-data source for the relevant code path, if it can carry malformed or compromised input.
So “private” changes who can attempt your request.
It does not change whether the receiving code checks ownership, constructs a safe query, restricts deserialization, or limits an outbound fetch.
The same source code does not become safer because its caller is now behind a segmentation matrix.
This is the part that gets lost in diagrams.
The diagram has an application zone, a data zone, some pleasantly assertive arrows, and the meeting feels complete.
Then an allowed workload sends a normal-looking request through the allowed arrow, and your application faithfully performs the unsafe thing it was written to perform.
Segmentation controls reachability. The application controls the consequences of allowed reachability.
4. “But We Scan the WAF Logs.”
Scan them.
Triage them.
They are operational evidence.
They are not evidence that code paths never exercised by a matching request are safe.
The most important missing discipline is usually not another dashboard.
It is triage, with an owner and a reason.
Your workflow puts triage after SAST.
A finding begins as a candidate, not a confirmed defect.
Severity asks for worst-case impact.
Reachability asks whether the flagged sink is invocable from real attacker-controlled input rather than dead code.
Exploitability context asks whether the component is internet-facing, authenticated-only, or behind a compensating control.
A finding that survives those filters goes to a developer with the specific line and reason.
One that doesn’t is suppressed with a documented reason.
Materially different from observing blocked traffic.
A WAF log records an event at the edge.
SAST gives a source-to-sink candidate in code.
Neither closes the other’s job ticket.
DAST adds a third view.
It sends crafted HTTP requests to a running staging or pre-production build and inspects the responses.
It sees deployed misconfiguration and behaviour depending on live authentication or session state.
Things SAST structurally cannot see from source alone.
It runs later precisely because a running build has to exist.
So a defect found there costs a build and deployment cycle more than one caught in review.
Keep all three forms of evidence distinct.
Traffic observed at a network enforcement point.
Code paths identified before execution.
Behaviour observed in a running build.
A spreadsheet calling all of them “WAF coverage” has become an aggressively formatted way to know less.
5. “But Secure Code Review Costs Too Much.”
So does a defect that reaches a running environment because nobody read the changed boundary.
Secure code review focuses attention on the lines where untrusted values cross into a query, a deserializer or an outbound URL.
A generic review assesses correctness and readability.
A trust-boundary checklist asks the question that matters: what happens when this request value, file, queue message or service response arrives here?
Review is also the only point in this workflow where a human can judge authorization intent.
Whether an endpoint should verify that the caller owns your record is a design question.
Automated tools help identify problematic patterns, and a downstream tool cannot reliably flag a missing check where it does not know the business rule in the first place.
Run SAST on every pull request or shared-branch commit, and gate the merge on the agreed result.
That catches flaws before a running environment exists.
Run DAST against a staging deploy or persistent test environment.
For payment-card environments, PCI DSS v4.0 Requirement 11.4 requires internal and external penetration testing annually and after significant infrastructure or application changes.
Those cadences change who tests, and when.
They don’t turn a WAF into review, SAST, DAST or penetration testing.
The economical answer is to find defects at the earliest point that can see them.
Review sees intent before anything runs.
SAST sees defined source-to-sink flows before deployment.
DAST sees runtime behaviour after deployment.
An edge filter sees request patterns at the edge.
Each catches a different portion of the problem, at a different cost.
Treating the cheapest visible tool as a replacement for the earlier work is not savings.
It is moving the cost to a later stage and losing information on the way.
6. “But the WAF Would Catch a Malicious Request Anyway.”
This assumes the maliciousness is visible in your request.
Unsafe deserialization is a useful correction.
A byte stream can reach a deserializer whose design permits arbitrary object construction.
The safer application control is a restricted class allowlist, or isolation in a sandboxed process with reduced permissions.
Your WAF does not alter which classes the application permits, or what your process can reach after accepted input is deserialized.
SSRF is your network-facing correction.
An application can receive a URL that is syntactically permitted and points somewhere it should not fetch.
For a finite, known destination space, allowlist the permitted domains or IPs.
For dynamic webhook destinations, OWASP says an allowlist cannot be used.
A denylist of link-local and loopback ranges becomes the best available option, and OWASP explicitly says it is not an impenetrable wall.
Then apply the actual egress control.
Place your service that fetches user-supplied URLs on a segment with no route to internal-only resources.
That doesn’t pretend the SSRF bug vanished.
It ensures a bypass has nowhere useful to land.
AWS IMDSv2 is a concrete version of the same idea.
IMDSv1 answered GET requests from the instance’s local network stack.
IMDSv2 requires a session token obtained with a PUT request, and returns it with an IP packet TTL of 1 by default.
A proxy relay decrements that TTL; a response reaching zero is discarded.
Your control narrows a particular relay path.
It doesn’t prove every server-side fetch feature is correct.
A request can be completely ordinary at the WAF and still be dangerous because of what the application does next.
Which is why the place of enforcement matters.
7. No, This Does Not Mean the WAF Has No Value.
It has real value.
A WAF is useful as a compensating and volumetric control.
An inline IPS can alert or drop based on a rule header and content matches.
Suricata’s IPS mode defaults to fail closed on parsing errors, blocking traffic it cannot fully evaluate.
That is a meaningful conservative behaviour at an inline enforcement point.
Use default-deny network policy.
Place rules most likely to match early, where a sequential firewall evaluates first match.
Put firewall changes through formal change management.
Review the ruleset.
PCI DSS v4.0 Requirement 1.2.7 requires at least a six-month review cadence, and Requirement 11.4.5 requires segmentation controls to be verified by penetration testing.
Those are network-control obligations.
Also recognize what they do not establish.
A stateful firewall tracks a connection through a 5-tuple: source and destination address, source and destination port, connection state.
An application-proxy gateway can make two separate connections.
Client to proxy, proxy to destination, and can decrypt, inspect and re-encrypt TLS payloads between them.
More inspection capability than simple packet filtering.
Still not an oracle for application intent.
More visibility into a request does not create the missing ownership rule.
Or choose the correct parameterization.
Or restrict a class allowlist.
Or remove an internal route from the fetching service.
The honest position is stronger than the dismissive one.
Deploy and operate the WAF well.
Then stop assigning it work at the code boundary that only the code boundary can perform.
8. What You Should Actually Do.
Run secure code review on changed code, with a checklist for request, file, queue-message and service-response trust boundaries.
Ask whether authorization belongs at the endpoint, and whether every query, deserialization entry point and outbound URL construction is safe by construction.
Run SAST against every pull request or shared-branch commit, gated on merge. Triage candidates by severity, reachability and exploitability context.
Assign surviving findings to an owner with the specific line and danger.
Suppress non-actionable results with a documented reason, not with the silence normally reserved for a broken office printer.
Run DAST against a running staging or pre-production build. Use periodic penetration testing for stable releases where cadence or obligation requires it, and treat bug bounty work as a supplement rather than a replacement for systematic testing.
Enforce server-side authorization for the actual resource and operation. Use parameterized data access wherever the interpreter supports it.
Validate input syntactically and semantically as early as it arrives, and do not mistake validation for your primary injection control.
For SSRF-capable features, allowlist known destinations. Where destinations are genuinely dynamic, apply the deliberately weaker denylist OWASP describes, and restrict egress so the fetching service cannot reach internal-only resources.
Use IMDSv2’s token and TTL behaviour as a further boundary, not as a reason to stop reviewing outbound fetches.
Deploy the WAF with monitored blocking rules. Triage its logs.
Treat it as a compensating layer.
When a confirmed finding is fixed, verify it with the targeted SAST rule, DAST test case or follow-up penetration-test item before closure.
Fix at the trust boundary where unsafe interpretation occurs.
Not with a downstream filter that leaves sibling paths exposed.
9. The Actual Model Is Not Complicated.
The durable claim is simple.
A filter observing HTTP cannot generally prove the safety of code that processes permitted requests.
Managed-rule identifiers, scanner coverage and current vendor behaviour are dated evidence.
Your boundary is not.
Network security filters and segments traffic before it arrives. Application security controls the code paths, authorization logic, input handling and egress behaviour that decide what accepted traffic can do.
Use the WAF.
Use default-deny segmentation.
Use formal rule changes and review.
Then review code, run SAST, run DAST against a running build, enforce authorization on the server, parameterize data access, isolate SSRF egress, triage findings, and verify the fix.
They aren’t redundant controls.
They are the load path from “your request was allowed through” to “your application did not do something unsafe with it.”
Objections
- Secure code review is where a reviewer can judge whether an authorization check belongs in a changed endpoint.
- SAST traces defined untrusted-data sources to dangerous sinks before code runs, while DAST observes a running build from the outside.
- Parameterized queries keep substituted values from changing a query's structure; string filtering does not provide that property.
- SSRF controls need both destination validation and an egress boundary that leaves a bypass nowhere useful to reach.
- A WAF or inline IPS is a compensating traffic control whose blocking rules do not repair the application behind it.
- A finding is closed only after a targeted verification scan or test confirms that the boundary fix holds.
What this does not say
A WAF remains a useful compensating and volumetric control, but it cannot reliably understand every authenticated business action or unsafe server-side behavior.
Raised in review
- Can a WAF replace secure code review?
- No. Secure code review examines a changed endpoint before it runs and can ask whether its business logic requires an ownership or authorization check. A WAF evaluates traffic reaching its enforcement point; it cannot determine from a request alone whether the caller should be allowed to access a particular record. Review therefore covers design intent that traffic filtering cannot infer.
- What does SAST find that a WAF cannot see?
- SAST analyzes source code, bytecode, or a build artifact without executing it. It can trace a defined untrusted-data source, such as a request parameter or file read, to a sink such as query execution, process spawning, or a URL fetch call, and flag a path without a recognized sanitizer. A WAF does not inspect those internal source-to-sink paths.
- Why does DAST need a running build?
- DAST exercises a deployed staging or pre-production application by sending HTTP requests and inspecting responses. It needs a running build because it evaluates runtime behavior, including deployed misconfiguration and behavior dependent on live authentication or session state. That gives DAST coverage that static analysis cannot provide, but it also means the defect has already passed through a build and deployment cycle.
- Why is filtering suspicious SQL text not the same as parameterized access?
- A denylist attempts to recognize dangerous characters, strings, or patterns before a value reaches a database. A parameterized query or prepared statement instead prevents the substituted value from changing the query structure, regardless of its content. OWASP describes denylist filtering as a common mistake because it can be bypassed and can reject legitimate values such as the surname O'Brian.
- Does input validation stop every injection problem?
- No. Syntactic validation confirms a value has an expected shape, while semantic validation asks whether that valid value is appropriate for the request. OWASP says validation should occur as soon as external data is received, but should not be the primary method for preventing injection. A well-formed value can still be dangerous to an interpreter, so parameterization or a safe format remains necessary.
- Why is SSRF an egress-control problem as well as an input problem?
- An SSRF-capable feature can make server-side requests to destinations selected through untrusted input. When permitted destinations are finite, an allowlist rejects all others. For dynamic webhook destinations, OWASP says an allowlist may be unavailable and a denylist of dangerous ranges is only a weaker fallback. Segmentation then limits damage by ensuring the fetching service has no route to internal-only resources.
- What does IMDSv2 change for an SSRF path?
- AWS Instance Metadata Service version 2 requires a session token obtained with a PUT request. Its response uses an IP packet Time To Live of 1 by default, so a response cannot survive a relay through a reverse proxy or similar intermediary: each hop decrements the TTL and a value reaching zero is discarded. This is a defense-in-depth control, not proof that the application has no SSRF flaw.
- What is a WAF or inline IPS actually good for?
- A WAF or inline IPS can apply blocking rules to traffic at a network chokepoint and can reduce exposure to known or recognizable request patterns. Inline IPS mechanisms can use signature rules, anomaly baselines, or stateful protocol analysis. Those controls are useful compensating layers, but their view is traffic behavior rather than the application’s complete authorization intent or every server-side execution path.
- Why are managed OWASP rules not a complete application-security programme?
- Managed rules can identify some known request patterns, but they do not make a complete inventory of every source-to-sink path, authorization decision, deserialization operation, or outbound fetch in an application. OWASP guidance treats input validation as a supporting control rather than the primary prevention method for injection. Managed traffic rules therefore complement code and runtime testing; they do not replace them.
- If an application is private, does it still need application security testing?
- Yes. Network segmentation restricts which zones may reach an application; it does not change the behavior of a request accepted from an allowed zone. NIST recommends deny-by-default firewall policy, and PCI DSS v4.0 requires unnecessary traffic at the cardholder data environment boundary to be denied. Those rules reduce reachability, while application testing still examines the permitted code paths.
- When can a vulnerability finding be closed?
- A finding can be closed after the defect is fixed at the trust boundary where unsafe interpretation should have been prevented and a verification step confirms the fix. The verification can be a targeted re-run of the SAST rule, the DAST test case that found it, or a follow-up penetration-test item. Closing a ticket on a developer assertion alone does not establish that sibling paths remain protected.
- How should WAF blocking rules be operated?
- Blocking rules require monitored operation, formal change management, and review of the evidence they produce. NIST SP 800-41 Rev. 1 says inbound and outbound traffic not expressly permitted by firewall policy should be blocked, and says changes should follow a formal change-management process. WAF or IPS logs can support triage, but logs are evidence of observed traffic rather than proof that unobserved code paths are safe.