Category: Scanners vs Research

Why scanners find noise, what real testing looks like, and the case for verification.

  • How to Test Access Control: A Step by Step Method for Web Apps and APIs

    How to Test Access Control: A Step by Step Method for Web Apps and APIs

    Access control is the control that fails most often and the one automated tooling is worst at checking. The reason is simple: a broken access control request is not malformed, so there is no bad pattern to detect. Deciding it is a bug requires knowing who was supposed to be allowed. This guide covers how to test access control in a web application or API, in an order you can actually work through, and what a correct result looks like at each step.

    Before you start: build the matrix

    Access control testing is comparison testing, so the first job is having things to compare. You need accounts and you need an inventory.

    • At least two accounts at the same level, so you can test one member against another. A single account cannot reveal a horizontal bug.
    • One account per privilege level the product has: member, administrator, billing owner, support, and anything internal.
    • Two tenants if the product is multi tenant. Two workspaces owned by different people is the only way to test the boundary that matters most to your customers.
    • A list of objects and who owns them. Note the ids created by each account. You will use this constantly.
    • A capture of normal traffic per level. Drive the application as each account with a proxy or the browser network tab recording. That capture is your test suite.

    Write down, in one table, which roles are meant to be able to do what. Most teams have never written this down, and the act of writing it usually surfaces two or three rules nobody had agreed on.

    How to test access control, step by step

    1. Swap object ids between accounts

    Signed in as user A, request the objects owned by user B. Change the id in the path, the query string, the JSON body, and any header that carries one.

    GET /api/notes/4121
    Authorization: Bearer tokenForUserA
    
    expected: 403 Forbidden or 404 Not Found
    finding:  200 OK with user B's data

    Repeat per verb, because read and write are authorized separately. A GET that is correctly denied says nothing about the PATCH or DELETE on the same object.

    2. Replay privileged requests with an unprivileged token

    Take the capture from the admin account and send those exact requests with a member token. This is the fastest test in the whole list and it finds the most serious bugs, because a route that only checks that you are signed in will answer anyone who is.

    3. Try to write fields you should not control

    Add attributes to bodies that do not document them, then read the record back to see whether the value stuck.

    PATCH /api/users/me
    { "display_name": "Sam", "role": "admin", "workspace_id": 9 }

    The usual candidates are role, is_admin, plan, scopes, owner_id, tenant_id, and verified. A response that echoes your value back is not proof on its own. Fetch the object again as a different account to confirm the change persisted.

    4. Follow every object into its quiet paths

    The direct fetch is the route people remember to protect. The same record is usually reachable through several others.

    • List and search endpoints, which often filter in the interface rather than the query
    • Export and report jobs, which run in the background with service credentials
    • File downloads and signed links, where the link may outlive the permission
    • Notification emails and webhooks, which quote object contents to whoever is subscribed
    • Older API versions still routed and no longer maintained

    5. Test permission over time, not just at one moment

    Access control has a lifecycle, and testing at a single point misses the whole class of stale authority.

    • Demote an account, then reuse the token it was issued before the change
    • Remove a member from a workspace, then replay their earlier requests
    • Cancel an invitation, then accept it
    • Delete an object, then request it directly by id, and check restore and undelete paths

    6. Check the tenant boundary explicitly

    With an account in workspace A, try to read, write, and invite into workspace B. Then do it through the quiet paths from step 4. Cross tenant leakage is the finding that ends enterprise deals, and it is frequently absent from an application’s test suite entirely.

    A correct response is a denial you can prove, not the absence of a link in the interface. If the button is hidden but the endpoint answers, the control does not exist.

    What a correct result looks like

    • 403 or 404 on every unauthorized request. Prefer 404 for objects the caller should not know about, since a 403 confirms the record exists.
    • Denials that come from the server, reproducible outside the browser with a raw request.
    • Consistent answers across paths. If the direct fetch denies and the export includes the record, the control is not enforced, it is decorated.
    • A test per object route asserting that user A cannot reach user B’s object. Without these, the next refactor quietly reopens what you just fixed.

    Where automation helps and where it does not

    Scanners are good at the parts with a signature: missing authentication on a route, a directory that lists, a known vulnerable component. They struggle here because every request in this guide is legal. The tool has no way to know that note 4121 belongs to someone else, or that only the billing owner should change a plan. That knowledge is specific to your application and it does not exist anywhere in the code in a checkable form. More on where scanners stop and research starts is here.

    What can be automated is the part that is mechanical once the intent is understood: holding several identities at once, enumerating every object and route, and replaying each request as each account. That is a lot of combinations and exactly the sort of work people skip when a release is due. It is also the direction we are building in. As an early signal, a frontier model drove the full methodology on its own and identified and verified real access control and injection issues in test applications it had not seen before. We are still early and it needs supervision, but the shape of the work suits a system that can reason about what an application is for rather than what its inputs look like.

    If you do only one thing from this guide, do step 2. Record what an administrator does, replay it as an ordinary member, and see what answers. It takes an afternoon and it finds the bugs that matter. Read more about how UnboundCompute works.

    Frequently asked questions

    How do you test access control in a web application?

    Create at least two accounts at the same level plus one per privilege level, record the objects each one owns, and capture the normal traffic of each. Then run six checks: swap object ids between accounts, replay admin requests with a member token, try to write fields such as role that you should not control, follow each object into list, export and download paths, retest after permissions change, and probe the tenant boundary. A correct application answers 403 Forbidden or 404 Not Found every time.

    What is the fastest access control test to run?

    Record everything an administrator account does, then send those exact requests using an ordinary member token. It takes an afternoon and it finds the highest severity issues, because any route that checks only that you are signed in will answer whoever asks. Hiding a button removes the path a normal user takes to an endpoint, not the endpoint itself.

    Why can scanners not find access control bugs?

    Because the requests are legal. Every field has the right type, the session is valid, and the response is a clean 200 OK. A scanner works by comparing traffic against known bad patterns, and there is no pattern here to match. Knowing that a particular record belongs to a different user, or that only a billing owner may change a plan, is knowledge about your specific application that does not exist in the code in any checkable form.

    How many test accounts do I need for access control testing?

    At least four in most products. Two accounts at the same level, because a single account cannot reveal a horizontal bug where one member reads another member’s data. One account per elevated level, such as administrator or billing owner. And if the product is multi tenant, a second workspace owned by an unrelated person, since cross tenant access is the failure customers care about most and the one least often covered by an existing test suite.


    Put an autonomous researcher on your own systems

    UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, and a say in what it looks for. If your team ships software worth pressure testing, apply to the design partner program.

  • SAST vs DAST vs IAST, what is the difference?

    SAST vs DAST vs IAST, what is the difference?

    If you have shopped for application security tools, you have run into the alphabet soup of SAST, DAST, and IAST. The sast vs dast question is the one most teams start with, but IAST sits in the middle and changes the answer. This post gives plain definitions for all three, shows what each catches and misses, and is honest about where they fall short.

    The short version of sast vs dast vs iast

    The three tools differ by where they stand and what they can see.

    • SAST (Static Application Security Testing) reads your source code without running it. It looks for dangerous patterns in the text of the program.
    • DAST (Dynamic Application Security Testing) tests the running app from the outside, like an attacker with no source code. It sends requests and reads responses.
    • IAST (Interactive Application Security Testing) watches from inside the running app. An agent sits in the process and sees both the incoming request and the line of code that handles it.

    SAST: reading the source code

    SAST parses your code and models how data flows through it. It traces a value from where it enters, such as a request parameter, to where it gets used, such as a database query or an HTML response. If tainted input reaches a dangerous function without being cleaned, SAST flags it.

    Here is the kind of flow SAST is good at spotting:

    name = request.args.get("name")
    query = "SELECT * FROM users WHERE name = '" + name + "'"
    db.execute(query)

    The user controls name, it lands in a SQL string with no escaping, and SAST follows that path from input to sink.

    What SAST catches

    • Injection patterns: SQL, command, and template injection where input flows into a sink.
    • Hardcoded secrets, weak crypto calls, and unsafe deserialization. Spotting a key or password committed straight into source is a classic static analysis check, and our free secret scanner runs that same kind of pattern check over code you paste in.
    • Bugs on code paths that are hard to reach with traffic, since SAST reads every branch whether or not it runs.

    What SAST misses

    • Anything that depends on configuration or the live environment. A query that looks unsafe may sit behind a parameterized layer SAST cannot model.
    • Logic that lives in a framework, a stored procedure, or a third party library the scanner does not parse.

    DAST: testing the running app from outside

    DAST treats the app as a black box. It crawls the pages, finds inputs, and throws payloads at them to see how the app reacts. If a request returns a database error or a reflected script, DAST records a finding.

    A simple DAST probe for reflected cross site scripting looks like this:

    GET /search?q=<script>alert(1)</script> HTTP/1.1
    Host: acmenotes.example

    If that <script> tag comes back in the HTML response unescaped, the app is reflecting raw input and DAST flags it.

    What DAST catches

    • Real behavior of the deployed app, including server config, headers, and TLS settings.
    • Reflected and stored injection, broken authentication flows, and missing security headers.
    • Issues that only show up once everything is wired together.

    What DAST misses

    • Code paths it never reaches. If the crawler does not find a form or an API route, that route is never tested.
    • The exact line of code at fault. DAST tells you the app misbehaved, not where in the source to fix it.
    • Bugs that need a valid login or a specific account state the scanner cannot reproduce.

    IAST: watching from inside while the app runs

    IAST puts an agent inside the running process, often through the language runtime. As traffic flows through the app, the agent sees the request, follows the data through the code that executes, and watches it reach a sink. It is dynamic like DAST, but with the inside view DAST lacks. So it can say something precise: this request reached this query on this line with this tainted value. That pairing is its main advantage.

    What IAST catches

    • Injection and input flaws confirmed against code that actually ran, so fewer guesses.
    • The specific file and line, which makes the fix faster than with DAST alone.
    • Flaws deep inside libraries, since the agent watches data move through them at run time.

    What IAST misses

    • Code that is never exercised. IAST only sees paths that real traffic or tests drive, so coverage depends on how thoroughly the app is used during testing.
    • Languages and runtimes the agent does not support, since instrumentation is tied to the platform.
    • Bugs outside the instrumented process, such as flaws in a separate service.

    Side by side: sast vs dast vs iast

    • SAST. Sees source code, does not run the app. Strong on coverage of every branch. Weak on run time and config reality.
    • DAST. Sees outside behavior, runs the app, needs no source. Strong on real deployed behavior. Weak on pointing to the exact code.
    • IAST. Sees inside the running app, needs runtime access. Strong on precise, confirmed findings. Weak on coverage of paths that never run.

    Where false positives come from

    Each tool gets noisy for its own reason.

    • SAST flags a path that looks dangerous but is safe, because it cannot see that a value was validated in a way it does not model, or that the path is dead code.
    • DAST reads a response and guesses. A database error in the page can be a leftover string, not proof of injection, so it raises a finding that is not real.
    • IAST is usually the quietest, because it confirms a finding against code that ran. Even so, it can mistake a safe sanitizer for a missing one if it does not recognize the cleaning function you use.

    The cost is real. Every wrong alert is time a developer spends ruling it out, and a backlog of noise trains teams to ignore the tool.

    The honest limit: none of them understand business logic

    Here is the part the vendor pages skip. All three look for known shapes of bugs. None understands what your app is supposed to do.

    Pattern matchers find the bug they were told to look for. They do not ask whether a user who can read invoice 41 should be able to read invoice 42.

    Consider GET /api/invoices/42 where the logged in user only owns invoice 41. Nothing in that request is malformed. No script tag, no SQL, no broken header. SAST sees clean code, DAST sees a normal 200 response, and IAST sees a safe query running. They all agree the request is fine, and they are all wrong, because the app forgot to check who owns invoice 42. This is broken access control, one of the most common serious bugs in real apps, and the scanners miss it because there is no pattern.

    For more on this gap between pattern matching tools and real reasoning about an app, read scanners vs research.

    So which one do you need?

    For most teams it is not one tool but a stack. SAST runs early on every commit and catches obvious sink bugs before they ship. DAST runs against a deployed build and shows how the real app behaves. IAST rides along with your existing tests and gives precise findings on the paths your traffic touches. They overlap, and that overlap is fine, because each one fails in a different place.

    What none of them replaces is a tester who reads the app’s logic and asks whether its assumptions hold. That assumption testing is exactly the kind of work an autonomous researcher is built to do, looking past fixed payload lists to the rules an app quietly trusts. Read how we think about it on our about page.

    Frequently asked questions

    What is the difference between SAST, DAST, and IAST?

    SAST reads your source code without running it and looks for dangerous patterns in the text. DAST tests the running app from the outside like an attacker with no source code. IAST puts an agent inside the running process so it sees both the incoming request and the line of code that handles it. The OWASP guidance on source code analysis tools covers the static side in more depth.

    Which is better for finding bugs, SAST or DAST?

    Neither is strictly better because they fail in different places. SAST covers every code branch whether or not it runs but cannot see live config or runtime reality, while DAST shows real deployed behavior but cannot point to the exact line of code or reach routes its crawler never finds. Most teams run both rather than picking one.

    Why does IAST usually have fewer false positives?

    IAST confirms a finding against code that actually ran, watching a tainted value reach a specific sink on a specific line, so it guesses less than DAST or SAST. It can still misfire if it does not recognize a safe sanitizer you use, and it only sees paths that real traffic or tests exercise.

    Can SAST, DAST, or IAST find broken access control?

    Usually no. A request like GET /api/invoices/42 from a user who only owns invoice 41 is well formed, returns a normal 200, and runs a safe query, so all three tools see nothing wrong. They look for known bug shapes and do not understand which user should be allowed to read which object.


    Put an autonomous researcher on your own systems

    UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, a say in what it looks for, and founding pricing. If your team ships software worth pressure testing, apply to the design partner program.

  • What is automated penetration testing?

    What is automated penetration testing?

    If you run a web app or an API, you have probably heard the phrase tossed around in security pitches. So what is automated penetration testing, and how is it different from the vulnerability scanner you may already run? In short, it is software that pokes at your application the way an attacker would, then tries to confirm what it finds, instead of a person doing every step by hand.

    This guide is for people who are new to the topic. We will define the term, compare it to a manual pentest and to a plain scanner, and be clear about what each tool is good at and where it falls short.

    What is automated penetration testing, in plain terms

    A penetration test, or pentest, is an authorized attempt to break into a system so you can fix the holes before a real attacker finds them. A human tester explores the app, forms a theory about what might break, and tries to exploit it. Automated penetration testing hands much of that loop to software. The tool maps the application, picks targets, sends crafted requests, and reports what got through.

    The word that matters here is exploit. A good automated pentest does not just say “this parameter looks risky.” It tries to actually use the weakness and shows you the result.

    The difference that counts is proof. A flag says maybe. A working exploit says yes, and here is the evidence.

    How it differs from a manual pentest

    A manual pentest is run by a person, often over one or two weeks, against a defined scope. Humans are good at understanding what an app is for. They read the screen, guess at business rules, and chase odd behavior that no rulebook predicted.

    Automation trades some of that judgment for speed and repeatability. Here is the honest trade:

    • Speed. Software can test thousands of requests in the time a person tests a handful.
    • Repeatability. You can run the same checks every night and on every deploy, not once a year.
    • Coverage of known classes. It is steady at the well understood bugs, like reflected injection or a missing access check on a predictable URL.
    • Weaker on context. It struggles with rules that only a human reading the app would know, such as “a trial account must never export the full customer list.”

    The two are not rivals. Many teams run automation often and bring in human testers for deep, scoped work on the parts that matter most.

    How it differs from a plain vulnerability scanner

    This is the comparison most people get wrong, so it is worth slowing down. A vulnerability scanner checks for known issues and reports anything that matches a signature. It might flag an out of date library, an open port, or a parameter that reflects input back to the page. That is useful, but a scanner usually stops at “this looks suspicious.”

    An automated pentest goes one step further and tries to prove the issue is real. Take a classic example. A scanner sees this request and notices the id value is reflected in the response:

    GET /api/invoices?id=1042
    Authorization: Bearer trial-user-token

    The scanner says: possible insecure direct object reference, please review. An automated pentest treats that as a theory to test. It changes the value and watches what comes back:

    GET /api/invoices?id=1043
    Authorization: Bearer trial-user-token
    
    HTTP/1.1 200 OK
    { "id": 1043, "customer": "Acme Notes", "total": 8800, "card_last4": "4242" }

    Now there is evidence. The trial user just read another customer’s invoice. That is no longer a maybe. It is a confirmed access control bug with a request you can replay. If the deeper reading on this distinction is what you are after, the scanners vs research category goes through it in more detail.

    Flagging versus verifying

    Hold this difference in your head, because it shapes everything else:

    • A scanner flags. It hands you a list of candidates ranked by severity, and a human has to check each one.
    • An automated pentest verifies. It tries the attack and keeps only the findings it could actually reproduce.

    The most useful tools sit on the verifying side. A short list of proven bugs is worth more than a long list of maybes, because every false alarm costs someone an hour of triage.

    What automated penetration testing is good at

    Used well, it earns its place. It is strong at:

    • Breadth. Checking every endpoint, every parameter, on a schedule a human could not keep.
    • Regression. A confirmed bug can become a repeatable check that watches for the same hole reappearing after a future deploy.
    • Fast feedback. Running on each release means a new flaw gets caught in days, not at the next annual review.

    Where it falls short

    Honesty matters more than the sales pitch, so here are the real limits.

    Logic bugs

    The bugs that hurt most often live in business logic, and those are the hardest to automate. Consider a checkout flow that applies a discount code. A tool that only sends known payloads will not think to apply the same code twice, or to set the quantity to a negative number so the total drops below zero. Those attacks come from understanding what the app is trying to do, then asking what happens if you bend a rule. A fixed payload list does not reason that way.

    Context and intent

    Software does not know your business rules unless someone teaches it. It cannot tell that a field labeled role should never be editable by the customer, or that an internal admin route was left exposed by accident. Without that context, it tests the requests it can see and misses the ones that only make sense once you understand the product.

    False positives and noise

    Tools that flag without verifying drown teams in noise. After enough false alarms, people stop reading the report, and a real finding gets lost in the pile. This is exactly why the verifying approach matters: proof cuts the noise.

    What good looks like

    If you are choosing a tool, look past the feature list and ask one question: does it prove its findings? The better systems do not just match patterns. They learn how the app is meant to work, form an idea about where that logic could break, design a test, and then confirm the result with concrete evidence before they bother you. Understand, assume, experiment, verify.

    The highest impact bugs come from understanding the app, not from matching a known string. That is the bar worth holding any tool to.

    Closing

    So, to answer the question plainly: automated penetration testing is software that attacks your app like an attacker would and, in its best form, proves what it finds rather than just listing suspects. It is fast and tireless on known issues, and weaker on logic and context, which is where a human or a smarter system earns its keep. This is the gap UnboundCompute is built to close, an autonomous researcher that tests the assumptions your app makes and proves a finding with hard evidence before reporting it. You can read more on the about page.

    Frequently asked questions

    Is automated penetration testing the same as a vulnerability scan?

    No. A vulnerability scanner flags anything that matches a known signature and usually stops at “this looks suspicious,” while automated penetration testing goes further and tries to actually exploit the weakness, then keeps only the findings it could reproduce. The difference is proof: a scanner hands you candidates to triage, an automated pentest hands you confirmed bugs with a request you can replay.

    Can automated penetration testing replace a human pentester?

    Not for everything. Automation wins on speed, breadth, and repeatability, so it is well suited to checking every endpoint on a schedule and catching well understood bugs like reflected injection or a missing access check on a predictable URL. It is weaker on business logic and context, which is why many teams run automation often and still bring in human testers for deep, scoped work.

    Why does automated penetration testing miss business logic bugs?

    Because logic bugs use input that is completely legal, so there is no signature to match. A tool that only sends known payloads will not think to apply a coupon twice or set a quantity to a negative number, since those attacks come from understanding what the app is trying to do rather than from a fixed payload list. You can read more on the OWASP Web Security Testing Guide.

    What should I look for when choosing an automated pentest tool?

    Ask one question: does it prove its findings? The stronger tools do not just match patterns, they learn how the app is meant to work, form an idea about where the logic could break, design a test, and confirm the result with concrete evidence before reporting it. A short list of proven bugs is worth more than a long list of maybes, because every false alarm costs someone time to triage.


    Put an autonomous researcher on your own systems

    UnboundCompute is an autonomous security researcher that reasons about how an application fits together and proves the access control and injection bugs it finds. We are opening a small number of founding design partner seats: private early access pointed at a staging target you choose, a say in what it looks for, and founding pricing. If your team ships software worth pressure testing, apply to the design partner program.