Prove the Attack Path Is Broken

A control change is not evidence that an Azure workload attack path has been removed. Build a scoped before-and-after test that checks authority, DNS, reachability, and the operation that matters.

The change ticket says “removed excessive access.” The role assignment is gone, the route was updated, and the review is marked complete.

What changed for the workload?

That question is not answered by the ticket status. A control-plane diff proves a configuration changed. It does not establish which identity the process uses now, where its hostname resolves, whether the service accepts the request, or whether an alternate path survived.

If the claim is that an attack path is broken, the evidence needs to include a test of that claim.

The Mental Model

A useful before-and-after comparison holds the test conditions steady while changing one known edge. For a workload-to-service path, record:

  • the source process and identity;
  • the target operation;
  • the service name and resolved destination;
  • the network protocol and port;
  • the identity/network configuration change;
  • the request result before and after.
flowchart LR W[Same workload context] --> I[Same identity under test] W --> D[Same DNS question] D --> P[Observed destination] P --> S[Same service and operation] C[Scoped control change] -. changes authority or reachability .-> S W -->|repeat comparable test| S

The graph is not an inventory of every possible route. It is a record of the claim you tested and the boundary around that claim.

A control is a proposed explanation. The before-and-after request is evidence of its effect.

How It Really Works

Write the claim so a failed test can disprove it

“We reduced attack paths” is not falsifiable. Try: “After removing the Blob data permission from principal A at scope B, workload C can still read its required test object, but cannot read the prohibited test object through hostname D.”

That claim names an allowed operation and a denied one. It also avoids equating application outage with security improvement. If the required read fails too, the change may have removed the dependency rather than narrowed it.

For a network change, specify the source, destination, protocol/port, and expected result. “Private Endpoint works” needs more detail: which service hostname, resolver context, address, and client operation? If the destination is a VM, name the listening port and test type. Record whether the source is the actual workload, a VM diagnostic, or a separate probe.

Capture the baseline before changing anything

Use a read-only, non-sensitive test object and an authorized environment. Capture the principal used by the workload, the requested operation, DNS answer from the workload’s resolver path, destination address used by the client, and connection/service outcome. Store tokens nowhere in the evidence pack.

Inspect the relevant configuration as well: role definition and assignment scope, identity attachment, DNS zone/links/forwarding, routes, NSGs, firewall policy, and target service restrictions where applicable. Include only the surfaces that affect this path. A sprawling export makes it harder to see the edge you intend to test.

Record time and versions. For Azure RBAC Role Assignments REST API, use a supported API version and include conditions when relevant; 2022-04-01 supports conditions. For Network Watcher, record current source support, region enablement, diagnostic mode, and required permissions. Neither tool replaces the application’s actual request.

Change one edge when practical

If you remove an identity assignment and simultaneously alter DNS, routes, and firewall policy, a failed request tells you the path changed, but not which change mattered. Incident containment may require several actions at once; a controlled architecture test should isolate the change where practical.

Keep the control-plane diff and the data-plane result separate. The diff is evidence of intent and scope. The request result is evidence of observed behaviour under test. If they disagree, investigate rather than choosing the more convenient one.

Retest the allowed and denied cases

Repeat the required operation from the same workload context. Then try one carefully selected operation or object that should remain denied. Keep the same source, identity, hostname, and protocol unless the change intentionally alters them. If the path is supposed to be unreachable, record an actual connection failure plus supporting route/policy evidence.

Do not claim “all access is gone” from one denied read. It says that request failed in that context and at that time. The service may have other data operations, identities, DNS answers, or network paths that were not part of the test.

Real-World Impact

The value of the evidence pack is not a more impressive audit trail. It is preventing the team from shipping a security change that simply moves the failure somewhere less visible.

For example, a role change might cause the target read to fail while the application falls back to a public endpoint and a different credential. If the only retest is “the app still works,” the path may be less contained than before. Conversely, a TCP failure from a diagnostic source might be caused by the target not listening, while the actual workload remains able to reach it through a proxy.

Use one compact record per claimed path:

Record itemBeforeAfter
Workload/process and principalIdentified runtime contextSame context or documented change
OperationRequired and prohibited test actionSame actions, same test data
DNSResolver and answer chainResolver and answer chain
NetworkSource, destination, protocol/port, resultComparable source and test
ChangeNone or baseline configurationExact role/DNS/route/policy diff
Service outcomeResponse and timestampResponse and timestamp
BoundaryUntested sources/targets/operationsRemaining paths and uncertainty

The table does not prove a whole estate is safe. It helps the owner state what improved, what remained available, and what still needs investigation.

Gotchas and Edge Cases

Before-and-after evidence can look objective while quietly comparing different tests. A new token, a different source VM, a changed DNS cache, or a different service operation can alter the outcome without demonstrating that the control worked.

Changes can have propagation and cache effects

Identity authorization, DNS, routes, and connections do not all change on the same clock. Verify the relevant service behaviour and record when each test ran. Do not invent a fixed convergence time or treat the first result after a change as permanent.

A denial can have the wrong cause

A permission error, timeout, DNS failure, and connection refusal are different results. Capture the service error or diagnostic reason. A generic “failed” status cannot tell you whether authority, name resolution, routing, a firewall, or the target itself stopped the operation.

Negative testing needs a safe boundary

Do not probe arbitrary tenant resources to see what else is reachable. Define the allowed and denied test targets in advance, use non-sensitive data, and obtain authorization. The prohibited case should be representative enough to test the control without turning the validation into uncontrolled discovery.

Evidence can go stale

A result applies to the tested configuration and time. New identity assignments, DNS links, endpoint changes, or routes can reopen a path. Decide which changes invalidate the record and who owns the retest. Otherwise, the evidence pack becomes another static diagram.

Best Practices

Keep a durable statement of the claim, the test tuple, the control change, and the result. Store enough information for another engineer to reproduce the request, but never include access tokens or sensitive payloads. Use the same workload context and safe test data before and after whenever possible.

Separate three conclusions: whether the configuration changed as intended; whether the required operation still works; and whether the prohibited path now fails for the intended reason. If any answer is unknown, say so. It is more useful than converting uncertainty into a green status.

Finally, treat the evidence as scoped and time-bound. Re-run it after a relevant edge changes and leave a list of untested identities, destinations, and operations. “We broke this path under these conditions” is a defensible result. “The workload has no attack paths” almost never is.

🍺
Brewed Insight: The proof isn’t that the role assignment disappeared or the firewall rule changed. The proof is the same workload request, tested again, with the intended dependency preserved and the unnecessary consequence removed.

Learn More