Skip to content

Business Logic Security Cheat Sheet / 业务逻辑安全速查表

Introduction / 介绍

Business logic vulnerabilities are flaws in the way an application implements its intended workflow. They aren't missing input sanitization or unescaped output. The code does what the developer told it to do, but what the developer told it to do doesn't match what the business actually needs. A user skips a required step, submits a request out of order, pays a negative price, stacks coupons in a way nobody planned for, or wins a race against the server's own bookkeeping.

业务逻辑漏洞是应用程序实现其预期工作流程时的缺陷。它们不是缺少输入清理或未转义的输出。代码做了开发者告诉它做的事情,但开发者告诉它做的事情与业务实际需求不匹配。用户跳过了一个必需的步骤,以错误的顺序提交请求,支付了负数价格,以没有人计划的方式堆叠优惠券,或者在与服务器的自身账目竞争中获胜。

Generic scanners often miss business logic flaws because they lack the application's business rules. Automated analysis can still detect some patterns, including workflow enforcement weaknesses. Combine tools with review and testing that checks the intended business process.

通用扫描器通常错过业务逻辑漏洞,因为它们缺乏应用程序的业务规则。自动分析仍然可以检测到一些模式,包括工作流程执行弱点。将工具与审查和测试相结合,以检查预期的业务流程。

This cheat sheet covers practical patterns for preventing business logic abuse. It's aimed at developers building features, not at penetration testers looking for them. For testing guidance, see the OWASP Web Security Testing Guide, Business Logic Testing section.

本速查表涵盖了防止业务逻辑滥用的实用模式。它面向构建功能的开发者,而不是寻找它们的渗透测试人员。有关测试指南,请参阅OWASP 网络安全测试指南,业务逻辑测试部分。

Key takeaways:

关键要点:

  • Always re-derive security-relevant values (prices, permissions, ownership) on the server. Client state is input, not truth.
    中文:始终在服务器上重新推导与安全相关的值(价格、权限、所有权)。客户端状态是输入,而不是真相。
  • Enforce workflows as explicit state machines. Don't rely on the UI to gate the order of steps.
    中文:将工作流程作为显式的状态机执行。不要依赖于 UI 来控制步骤的顺序。
  • Treat concurrency as a real threat. If two requests can race, assume they will.
    中文:将并发视为真正的威胁。如果两个请求可以竞争,则假设它们会这样做。
  • Rate-limit and monitor at the feature level, not just at authentication. Abuse-friendly features (referrals, coupons, password reset) need their own controls.
    中文:在功能层面进行速率限制和监控,而不仅仅是认证层面。对滥用友好的功能(如推荐、优惠券、密码重置)需要自己的控制机制。
  • Threat model from the business process, not just the technical architecture. Ask what happens if a user acts dishonestly, not just what happens if an attacker sends a malicious payload.
    中文:从业务流程而非仅仅是技术架构出发构建威胁模型。询问如果用户行为不诚实会发生什么,而不仅仅是攻击者发送恶意负载会发生什么。

Why Business Logic Flaws Are Different / 为什么业务逻辑漏洞是不同的

Most well-known web vulnerabilities (SQL injection, XSS, CSRF, path traversal) have a clear technical signature. A security scanner can fuzz parameters, look for reflected payloads, and produce a reasonable report. Business logic flaws often require additional knowledge of the intended workflow.

大多数已知的网络漏洞(SQL注入、XSS、CSRF、路径遍历)都有明确的技术特征。安全扫描器可以模糊参数,寻找反射负载,并生成合理的报告。业务逻辑漏洞通常需要了解预期的流程。

Consider an e-commerce checkout. The server accepts a request containing a product ID, quantity, and coupon code. Every input is validated: the product ID exists, the quantity is a positive integer, the coupon code matches a known pattern. No technical rule is broken. But the application recalculates the total from the client-submitted price instead of looking it up server-side, so a user can pay one cent for a television. That's a business logic bug, and no amount of input validation helps, because the input is syntactically perfect.

考虑一个电子商务结账过程。服务器接受包含产品ID、数量和优惠券代码的请求。每个输入都经过验证:产品ID存在,数量是正整数,优惠券代码匹配已知模式。没有违反任何技术规则。但是应用程序从客户端提交的价格重新计算总价,而不是在服务器端查找,因此用户可以用一美分购买电视。这是一个业务逻辑错误,因为输入在语法上是完美的,所以任何输入验证都无法帮助。

The patterns repeat across industries:

这些模式在各个行业之间重复:

  • A user-facing application assumes the client will respect the order of steps in a multi-step workflow, and exposes the endpoints for each step without checking whether the previous steps were completed.
    中文:一个面向用户的应用程序假设客户端会尊重多步骤工作流程中的步骤顺序,并且暴露每个步骤的端点,而不检查之前的步骤是否完成。
  • A system takes user-controlled data (price, account balance, user role) from the request body and trusts it, because the UI never shows the user how to change it.
    中文:一个系统从请求体中获取用户可控数据(价格、账户余额、用户角色),并信任它,因为用户界面从未向用户展示如何更改它。
  • An endpoint performs two operations (check balance, then debit) without a lock, so two concurrent requests both pass the balance check and both debit.
    中文:端点执行两个操作(检查余额,然后扣款)而不加锁,因此两个并发请求都通过了余额检查并都进行了扣款。
  • A feature intended to reward legitimate use (referrals, points, promo credits) has no controls against one user creating many accounts.
    中文:一个旨在奖励合法使用(推荐、积分、促销信用)的功能没有防止一个用户创建多个账户的控制措施。

Review these patterns against the application's business rules; a clean scanner report does not show that those rules are enforced.

将这些模式与应用程序的业务规则进行审查;干净的扫描报告并不能显示这些规则是否得到执行。

Always Re-derive Security-relevant Values Server-side / 始终在服务器端重新推导安全相关值

The single most effective defensive habit is this: if a value in the request influences price, access, ownership, or state, assume the client made it up and recompute it from trusted data.

最有效的防御习惯是:如果请求中的某个值影响价格、访问、所有权或状态,则假设客户端编造了它,并从可信数据中重新计算它。

Prices and Totals / 价格和总计

Never accept a price, subtotal, tax, or total from the client. Accept product identifiers and quantities only, and compute the rest on the server from your own database. The same applies to discounts: accept a coupon code, validate the code server-side, and apply the discount server-side. Don't accept a "discount amount" field from the request.

永远不要接受来自客户端的价格、小计、税或总计。只接受产品标识和数量,并在服务器上从自己的数据库中计算其余部分。同样适用于折扣:接受优惠券代码,在服务器端验证代码,并在服务器端应用折扣。不要接受请求中的“折扣金额”字段。

Client sends / 客户端发送 Server should / 服务器应该 Server should not / 服务器不应该
Product ID and quantity
产品ID和数量
Look up the price, compute line total
查找价格,计算行总价
Accept a price field and trust it
接受价格字段并信任它
Coupon code
优惠券代码
Validate and apply the discount itself
验证并应用折扣
Accept a "discounted total" field
接受“折扣后总价”字段
Shipping selection
选择运输方式
Compute cost from its own rate table
根据自身费率表计算费用
Accept a shipping cost field
接受运输费用字段
Tax-exempt flag
免税标志
Determine from account state
根据账户状态确定
Accept a client-supplied exemption
接受客户提供的免税

This pattern extends beyond commerce. A social app shouldn't accept a "post visibility" value from the client if the value is supposed to be derived from the user's privacy settings. A banking app shouldn't accept a "source account balance" that the client calculated.

此模式不仅限于商业。一个社交应用不应当接受客户端提供的“帖子可见性”值,如果该值应该由用户的隐私设置推导而来。一个银行应用不应当接受客户端计算的“源账户余额”。

Permissions and Ownership / 权限和所有权

Re-check ownership on every request that acts on a resource. Don't rely on the URL, the referrer, or a flag in the request body. The backend should ask: "Is the authenticated user actually allowed to perform this action on this object, right now?"

在每次对资源进行操作的请求中重新检查所有权。不要依赖于URL、引用者或请求体中的标志。后端应该询问:“认证用户现在是否真的被允许对此对象执行此操作?”

A common mistake is checking ownership once when a resource is loaded and then trusting subsequent requests that reference it. Each request is independent. Each request must be authorized on its own.

一个常见的错误是在资源加载时检查所有权一次,然后信任后续引用该资源的请求。每个请求都是独立的。每个请求都必须单独授权。

Identity and Role / 身份和角色

Never accept a user ID, tenant ID, or role from the request body unless the request is explicitly an administrative action by a privileged caller, and even then the value has to be validated against what the caller is allowed to manage. The identity of the acting user always comes from the server-side session or token, never from a field the user can edit.

除非请求明确是受特权调用者进行的行政操作,否则不应接受请求体中的用户ID、租户ID或角色,即使在这种情况下,值也必须与调用者被允许管理的值进行验证。执行用户身份始终来自服务器端会话或令牌,而不是用户可以编辑的字段。

Enforce Workflows as Explicit State Machines / 强制执行工作流程为显式状态机

Multi-step processes (signup flows, checkouts, approvals, KYC verification, password reset) are prime targets because developers often implement them as a sequence of pages that the UI walks the user through. If the UI is the only thing enforcing the sequence, an attacker who sends requests directly to each endpoint can skip steps, repeat steps, or reach terminal steps without the prerequisites.

多步骤流程(注册流程、结账、审批、KYC验证、密码重置)是主要目标,因为开发者通常将它们实现为UI引导用户通过的一系列页面。如果UI是唯一强制执行序列的东西,那么直接向每个端点发送请求的攻击者可以跳过步骤、重复步骤或在没有先决条件的情况下达到终端步骤。

Model State on the Server / 服务器上的模型状态

Every multi-step workflow should have an explicit state representation stored on the server, keyed to the user or session. Each transition should be validated against the current state.

每个多步骤工作流程都应该在服务器上存储一个显式的状态表示,以用户或会话为键。每个转换都应该与当前状态进行验证。

A minimal pattern:

一个最小模式:

  1. When the workflow starts, create a record with the initial state (e.g., awaiting_email_verification).
    中文:当工作流程开始时,创建一个记录,包含初始状态(例如,awaiting_email_verification)。
  2. Each step endpoint checks the current state, performs the action, and updates the state to the next valid value.
    中文:每个步骤端点检查当前状态,执行操作,并将状态更新为下一个有效值。
  3. If a request arrives for a step that doesn't match the current state, reject it.
    中文:如果收到一个请求的步骤与当前状态不匹配,则拒绝它。
  4. Terminal actions (submit order, approve, transfer funds) only run if the state is exactly what's required for that action.
    中文:终端操作(提交订单、批准、转账资金)仅在状态正好符合该操作要求时运行。

Don't Use Hidden Form Fields as State / 不要使用隐藏表单字段作为状态

A pattern that shows up repeatedly in real applications is storing the "current step" as a hidden field in the form, or in a cookie the client can read. This is not enforcement. The client can set it to whatever value it wants. State lives on the server, in storage the client can't write to.

在实际应用中反复出现的一种模式是将“当前步骤”作为表单中的隐藏字段或客户端可读取的cookie存储。这并不是强制执行。客户端可以将其设置为任何它想要的值。状态存在于服务器上,在客户端无法写入的存储中。

Reject Replays of Completed Steps / 拒绝已完成的步骤的重放

Once a step has been completed, later attempts to re-run it should fail. This matters especially for one-time operations like applying a signup bonus, redeeming a coupon, claiming a voucher, or accepting a referral reward. The cleanest pattern is to mark each such operation complete in persistent storage and treat attempts to re-run it as errors.

一旦一个步骤已完成,后来尝试重新运行它应该失败。这对于一次性操作(如应用注册奖励、兑换优惠券、领取代金券或接受推荐奖励)尤为重要。最干净的策略是在持久存储中标记此类操作已完成,并将尝试重新运行它视为错误。

Expire Partial States / 使部分状态过期

Workflows that pause for user input (email verification, bank transfer confirmation, multi-step KYC) should have a deadline. If the user hasn't progressed in some bounded time, invalidate the partial state and require them to start over. Long-lived half-completed workflows accumulate and are a frequent source of exploitable inconsistencies.

需要用户输入(如电子邮件验证、银行转账确认、多步骤KYC)的工作流程应该有截止日期。如果用户在一段时间内没有进展,则使部分状态无效,并要求他们重新开始。长期未完成的工作流程积累,是可利用的不一致性频繁来源。

Prevent Race Conditions on Sensitive Operations / 防止敏感操作上的竞争条件

Any operation that reads a value, makes a decision, and then writes a value is a potential race condition. If two requests can run at the same time, they can both read the old value, both decide the same thing, and both write. This is how loyalty points get drained, balances go negative, single-use coupons get redeemed many times, and one-per-account bonuses become many-per-account.

任何读取值、做出决定然后写入值的操作都是潜在的竞争条件。如果两个请求可以同时运行,它们都可以读取旧值,都做出相同的决定,并且都写入。这就是忠诚度积分被耗尽、余额变为负数、一次性优惠券被多次兑换、以及每个账户一次奖励变成多个账户的原因。

Identify the Critical Sections / 识别关键部分

Ask which operations have the shape "check a condition, then act on it". Typical examples:

询问哪些操作具有“检查一个条件,然后对其采取行动”的形状。典型示例:

  • Check balance, then debit.
    中文:检查余额,然后扣除。
  • Check that a coupon hasn't been used, then record its use.
    中文:检查优惠券是否未被使用,然后记录其使用。
  • Check that a user has fewer than N items, then add one.
    中文:检查用户拥有的物品数量少于N个,然后添加一个。
  • Check that a slot is available, then book it.
    中文:检查是否有可用的时段,然后预订它。
  • Check that a referral code hasn't been applied to this account, then apply it.
    中文:检查推荐码是否尚未应用于此账户,然后应用它。
  • Check whether a registration request is unique, then insert the new user record.
    中文:检查注册请求是否唯一,然后插入新用户记录。

Each of these is a race condition waiting to happen unless the check and the act are inside a single atomic operation.

除非检查和行动都在单个原子操作内,否则这些操作都是即将发生的竞争条件。

Use Database Transactions and Locks

The most broadly available fix is a database transaction with the right isolation level. At the default isolation level of most databases, the check and the update run separately and another transaction can slip between them. For operations that must be atomic, use one of:

  • A SERIALIZABLE transaction isolation level. The database will detect conflicts and roll back one of the competing transactions.
  • An explicit row lock (SELECT ... FOR UPDATE in PostgreSQL, MySQL InnoDB, and similar). The second caller blocks until the first one commits.
  • A conditional update (UPDATE ... WHERE balance >= amount) that succeeds only if the predicate still holds at write time. Check the number of affected rows. If zero, the check failed and the caller should see an error.

Use Idempotency Keys for External Actions

For external actions such as charging a card, use the provider's idempotency mechanism and reuse the same key when retrying the same operation. Follow its key-generation, parameter-matching, and retention rules; for example, Stripe rejects changed parameters and can treat a key as new after its stored record expires.

For your own API, scope stored keys to the authenticated caller and operation, bind them to the original request parameters, and reject reuse with different parameters. Authorize the request before returning a cached result; possession of a key is not authorization. Coordinate concurrent requests atomically so only one starts the action, and record its state and result durably. AWS describes caller-scoped identifiers, atomic processing, and parameter-mismatch checks.

Define the retry and retention window, including recovery when the external result is unknown. A local database transaction alone cannot make an external side effect atomic with saving its result. After the retry window expires, reconcile the external outcome before resubmitting.

定义重试和保留窗口,包括在外部结果未知时的恢复。仅本地数据库事务无法使外部副作用与保存其结果原子化。在重试窗口到期后,在重新提交之前协调外部结果。

Don't Assume "Fast Enough" / 不要假设“足够快”

A common rationalization is "the window between the check and the update is microseconds, no attacker can exploit it". Concurrent request tools can trivially send dozens of requests to arrive within a millisecond of each other. Modern bug bounty tooling sends them as a single multiplexed HTTP/2 request so they land at the server effectively simultaneously. If the window exists at all, assume it's exploitable.

一个常见的辩解是“检查和更新之间的窗口是微秒级,没有攻击者可以利用它”。并发请求工具可以轻易地发送数十个请求,使它们彼此之间相差不到一毫秒。现代漏洞赏金工具将它们作为单个多路复用的HTTP/2请求发送,从而在服务器上几乎同时到达。如果窗口确实存在,则假设它是可利用的。

Pattern Reference / 模式参考

Operation shape / 操作形状 Safe pattern / 安全模式
Read-modify-write on a single row
对单行进行读取-修改-写入
SELECT ... FOR UPDATE then UPDATE, inside a transaction
在事务中先 SELECT ... FOR UPDATE 然后 UPDATE
Conditional decrement on a counter
计数器的条件递减
UPDATE ... SET value = value - 1 WHERE value > 0, check affected rows
UPDATE ... SET value = value - 1 WHERE value > 0, 检查受影响行
One-per-user bonus
每个用户一次的奖金
Unique constraint on (user_id, bonus_type) and let the database reject duplicates
在 (user_id, bonus_type) 上设置唯一约束,并让数据库拒绝重复项
External non-idempotent call
外部非幂等调用
Provider-supported idempotency, with durable state for retries and recovery
提供者支持的幂等性,具有用于重试和恢复的持久状态
Cross-row consistency
跨行一致性
Serializable transaction with explicit retry logic
具有显式重试逻辑的序列化事务

Protect Abuse-friendly Features / 保护易于滥用的功能

Some features are inherently abuse magnets because they dispense value in response to user actions. Referral bonuses, promo codes, signup credits, free trials, email reminders, password reset flows, account recovery paths, and anything that sends messages or makes outbound requests. These need their own controls beyond the authentication controls on the rest of the app.

一些功能天生就是滥用磁铁,因为它们在用户操作后分发价值。推荐奖金、促销代码、注册信用、免费试用、电子邮件提醒、密码重置流程、账户恢复路径以及任何发送消息或进行出站请求的功能。这些功能需要比应用程序其余部分的身份验证控制更多的控制。

Abuse Patterns to Design Against / 设计时需防范的滥用模式

  • Multi-accounting. One human creates many accounts to claim one-per-account rewards multiple times. Ask whether your signup flow makes this trivially cheap and consider what signals you have to detect it.
    中文:多账号操作。 一个人创建多个账号以多次领取每个账号的奖励。询问您的注册流程是否使这变得极其便宜,并考虑您有哪些信号可以检测到这种行为。
  • Self-referral. A user refers themselves using a second account. Referral flows should check that referrer and referee are distinguishable humans, not just distinguishable accounts.
    中文:自我推荐。 用户使用第二个账号推荐自己。推荐流程应检查推荐者和被推荐者是否为可区分的人类,而不仅仅是可区分的账号。
  • Coupon stacking. Multiple promos that were each meant to be used alone get combined to push a price below cost. If your coupon engine allows stacking by default, it's probably a bug.
    中文:优惠券叠加。 多个原本单独使用的促销活动被组合在一起,将价格推低于成本。如果您的优惠券引擎默认允许叠加,这可能是错误。
  • Free trial resets. A user cancels and re-signs up repeatedly to stay on the free tier forever. Track trial eligibility by something more stable than an email address.
    中文:免费试用重置。 用户反复取消并重新注册以永远留在免费层。通过比电子邮件地址更稳定的东西跟踪试用资格。
  • Resource exhaustion. Features that send email, make outbound HTTP calls, trigger webhooks, or run expensive computations on demand are DoS vectors and spam vectors unless rate-limited.
    中文:资源耗尽。 发送电子邮件、发起HTTP调用、触发webhook或按需运行昂贵计算的功能是DoS攻击和垃圾邮件攻击的向量,除非有限制。
  • Enumeration through behavior. A password reset endpoint that returns different messages for valid and invalid emails leaks account existence.
    中文:通过行为进行枚举。 对于有效和无效电子邮件返回不同信息的密码重置端点泄露了账户存在。

Controls to Apply / 要应用的控制

  • Per-feature rate limits. A global rate limit at the edge is not enough. The signup-bonus endpoint, the referral endpoint, the promo-redemption endpoint each need their own limits.
    中文:按功能限制速率。 边缘的全局速率限制不足以。注册奖金端点、推荐端点、促销兑换端点每个都需要自己的限制。
  • Identity signals beyond email. Device fingerprints, payment-method fingerprints, phone number verification, and KYC verification all carry more signal than email addresses, which are cheap to create in bulk.
    中文:超越电子邮件的标识信号。 设备指纹、支付方式指纹、电话号码验证和KYC验证所携带的信号比电子邮件地址更多,因为电子邮件地址可以批量低成本地创建。
  • Audit trails on value-dispensing operations. Every issued credit, applied promo, or granted bonus should be logged with the triggering user, target user, IP, and timestamp. When abuse is suspected, the log is how you untangle it.
    中文:价值分发操作的审计跟踪。 每次发放的信用额度、应用的促销活动或授予的奖金都应该记录触发用户、目标用户、IP地址和时间戳。当怀疑滥用时,日志就是您解开谜团的方法。
  • Maximums at every layer. A per-action cap (e.g., one bonus per account) plus a per-account cap (e.g., total lifetime promo value) plus a per-source cap (e.g., per payment method or per device) gives defense in depth.
    中文:每一层的最大值。 每个操作的限额(例如,每个账户一个奖金)加上每个账户的限额(例如,总促销价值)加上每个来源的限额(例如,每个支付方式或每个设备)提供了深度防御。
  • Asymmetric consequences. Actions that give value should be harder than actions that don't. Making someone wait 30 seconds, or complete a CAPTCHA, to claim a reward is fine. The legitimate user clicks once and moves on; the automated abuser suffers a per-request cost.
    中文:不对称的后果。 给予价值的操作应该比不给予价值的操作更难。让某人等待30秒或完成一个CAPTCHA来领取奖励是可以的。合法用户点击一次就继续;自动化滥用者会遭受每个请求的成本。

Threat Model from the Business Process / 从业务流程中构建威胁模型

See the Abuse Case Cheat Sheet for identifying misuse scenarios.

查看滥用案例速查表以识别滥用场景。

Most threat modeling is done from a technical angle: data flow diagrams, trust boundaries, STRIDE categories. That's useful, but it misses the bugs where the code is technically correct and the process is the problem. Business logic threat modeling asks different questions.

大多数威胁建模都是从技术角度进行的:数据流图、信任边界、STRIDE类别。这很有用,但它忽略了代码在技术上正确而流程是问题的错误。业务逻辑威胁建模会提出不同的问题。

Questions to Ask Early / 早期需要提出的问题

  • What does a legitimate user do in this feature, step by step?
    中文:合法用户在这个功能中会按步骤做什么?
  • What does the system do at each step, and what assumption is it making about the user's intent?
    中文:系统在每一步会做什么,它对用户意图做出了哪些假设?
  • Which of those assumptions would benefit the user if they were false? (That's where the bugs live.)
    中文:如果这些假设是错误的,哪些会对用户有益?(这就是bug所在。)
  • Can the user do things out of order? Skip a step? Repeat a step?
    中文:用户能否改变顺序?跳过步骤?重复步骤?
  • Can two users act on the same object at the same time?
    中文:两个用户能否同时作用于同一对象?
  • Can one user act from two directions at once (e.g., two tabs, two devices)?
    中文:一个用户能否同时从两个方向进行操作(例如,两个标签页,两个设备)?
  • What does this feature produce that has value? (Credits, access, trust, messages, outbound requests.)
    中文:这个功能产生了哪些有价值的东西?(积分,访问权限,信任,消息,出站请求。)
  • If an adversary tried to exploit this feature for gain, how would they go about it?
    中文:如果对手试图利用这个功能谋取利益,他们会如何操作?
  • What invariant must always hold (e.g., "a coupon can only be used once", "an account balance can never be negative")? What enforces it, and is that enforcement atomic?
    中文:必须始终保持哪些不变量(例如,“优惠券只能使用一次”,“账户余额永远不会为负”)?是什么强制执行这些不变量,这种执行是否是原子的?

The "Dishonest User" Exercise / “不诚实用户”练习

Sit with the feature specification and imagine a user whose only goal is to extract maximum value while staying within the letter of the system's rules. What would they do? What combination of legitimate actions would produce an illegitimate outcome? This is different from thinking about a technical attacker. The dishonest user isn't sending malicious payloads. They're using the app exactly as designed, just in an order or volume the designer didn't anticipate.

与功能规范坐下来,想象一个唯一目标是提取最大价值同时遵守系统规则的用户。他们会怎么做?哪些合法行为的组合会产生非法结果?这与考虑技术攻击者不同。不诚实用户不会发送恶意负载。他们正在按照设计使用应用程序,只是顺序或数量是设计者没有预料到的。

Write Down the Invariants / 写下不变量

For any feature that handles value, ownership, or state, write down the invariants in plain English:

对于任何处理价值、所有权或状态的功能,用普通英语写下不变量:

  • A user cannot approve their own request.
    中文:用户不能批准自己的请求。
  • A coupon code is valid for one redemption per user, and never more than N total.
    中文:优惠券代码对每个用户有效一次,并且总次数不超过N次。
  • A transfer cannot complete if it would leave the source account below zero.
    中文:如果转账会使源账户余额低于零,则转账无法完成。
  • A workflow reaches the "approved" state only after both reviewer A and reviewer B have approved.
    中文:一个工作流程只有在A审查员和B审查员都批准后才会达到“已批准”状态。

Then for each invariant, identify exactly what code enforces it. If the answer is "the UI" or "the user won't try that", the invariant isn't actually enforced.

然后,对于每个不变量,确定确切是哪段代码强制执行了它。如果答案是“用户界面”或“用户不会尝试那样做”,则实际上并没有强制执行该不变量。

Authorization at the Business-logic Layer / 业务逻辑层授权

Technical authorization (is this user authenticated, do they have this role) is necessary but not sufficient. Many business logic bugs are authorization bugs in disguise: the user has the general permission to use a feature, but the specific action they're taking violates a rule the system didn't think to check.

技术授权(此用户是否已认证,他们是否有此角色)是必要的,但不足以满足要求。许多业务逻辑错误实际上是授权错误:用户有使用功能的通用权限,但他们正在执行的具体操作违反了系统未考虑检查的规则。

Contextual Authorization / 上下文授权

Check not just "can this user do X" but "can this user do X on this object, in this state, right now".

不仅要检查“此用户能否执行X”,还要检查“此用户是否能在当前对象、当前状态下立即执行X”。

Examples:

示例:

  • A manager can approve expense reports, but not their own.
    中文:经理可以批准费用报告,但不能批准自己的费用报告。
  • A reviewer can approve a pull request, but not if they authored it.
    中文:审阅者可以批准拉取请求,但不能批准他们自己创建的请求。
  • A user can cancel an order, but not after it has shipped.
    中文:用户可以取消订单,但不能在订单发货后取消。
  • A user can change their email, but not to an address already in use, and not without verifying the new address.
    中文:用户可以更改他们的电子邮件地址,但不能更改到已使用的地址,并且必须验证新地址。

These checks are part of the business logic, not the authentication layer. They typically require knowledge the auth middleware doesn't have. Keep them close to the operation they guard.

这些检查是业务逻辑的一部分,而不是身份验证层。它们通常需要身份验证中间件没有的知识。请将它们靠近它们所保护的操作。

Avoid "Implicit" Permissions / 避免“隐式”权限

If two features logically grant the same permission, make sure both are guarded. An internal admin endpoint that's checked carefully is no help if the same operation is reachable through a less-guarded public endpoint.

如果两个功能在逻辑上授予相同的权限,请确保两者都受到保护。一个经过仔细检查的内部管理员端点如果相同的操作可以通过一个保护较弱的公共端点访问,那么就没有帮助。

Map every sensitive operation to every entry point that can trigger it, and verify each entry point enforces the rules. When adding a new entry point (a new API version, an internal tool, a webhook handler), explicitly audit which business rules it needs to apply.

将每个敏感操作映射到可以触发它的每个入口点,并验证每个入口点是否执行了规则。当添加一个新的入口点(新的API版本、内部工具、webhook处理程序)时,明确审计它需要应用哪些业务规则。

For general access control guidance, see the Access Control Cheat Sheet and the Authorization Cheat Sheet. For the higher-sensitivity case of financial or state-changing transactions, see the Transaction Authorization Cheat Sheet.

通用访问控制指南,请参阅访问控制速查表和授权速查表。对于更敏感的财务或状态变更交易情况,请参阅交易授权速查表。

Validate Inputs for Business Meaning, Not Just Format / 验证输入的业务意义,而不仅仅是格式

See the Input Validation Cheat Sheet for validation controls.

有关验证控制,请参阅输入验证速查表。

Input validation typically focuses on format: is this a well-formed integer, is this within length limits, does it match a whitelist of characters. That catches injection attacks but not business logic abuse. The input can be perfectly formatted and still semantically invalid.

输入验证通常关注格式:这是一个格式良好的整数吗?它的长度是否在限制范围内?它是否匹配字符白名单。这可以捕获注入攻击,但不能防止业务逻辑滥用。输入可以格式正确,但语义上仍然无效。

Validate Ranges / 验证范围

A quantity can be a positive integer and still be wrong. A discount can be numeric and still absurd. Validate against the meaningful range for the business:

数量可以是正整数,但仍然错误。折扣可以是数值型,但仍然荒谬。验证业务的有意义范围:

  • Quantity must be at least 1 and no more than the stock available.
    中文:数量必须至少为1,且不超过库存数量。
  • Date must be in the future, not more than a year out, not on a blocked day.
    中文:日期必须在未来,不超过一年,且不在封锁日。
  • Amount must be positive, not more than the account allows, not above a regulatory limit.
    中文:金额必须是正数,不得超过账户允许的金额,且不超过监管限额。
  • Text length must match what the downstream system can accept, which may be shorter than a generic limit.
    中文:文本长度必须与下游系统可以接受的长度匹配,这可能比通用限制要短。

Validate Combinations / 验证组合

Fields that are individually valid can be collectively invalid. A booking for a valid room with a valid check-in date and a valid check-out date might still be invalid if the check-out precedes the check-in. A transfer between two valid accounts might still be invalid if the accounts belong to different customers and the user lacks permission to act on both.

单独有效的字段可能集体无效。一个有效房间、有效入住日期和有效退房日期的预订可能仍然无效,如果退房日期早于入住日期。两个有效账户之间的转账可能仍然无效,如果账户属于不同的客户且用户没有权限对两个账户都进行操作。

Write the validation rules that describe legal combinations and enforce them server-side. Unit-test the cases where each individual field is valid but the combination is not.

编写描述合法组合的验证规则,并在服务器端强制执行。对每个单独字段有效但组合无效的情况进行单元测试。

Don't Trust Derived or Hidden Fields / 不要信任派生或隐藏字段

Fields that are "not editable in the UI" are fully editable at the HTTP layer. Every field in a request is an input, including the ones in hidden form controls, disabled form controls, fields set by JavaScript, and fields you set in a previous response and expect to receive unchanged. If a field matters, validate it as if it came from an attacker, because it did.

在UI中不可编辑的字段在HTTP层中是完全可编辑的。请求中的每个字段都是输入,包括隐藏表单控件、禁用表单控件、由JavaScript设置的字段以及你在之前的响应中设置的并期望保持不变的字段。如果某个字段很重要,就像它来自攻击者一样对其进行验证,因为它确实可能来自攻击者。

Observability and Anomaly Detection / 可观察性和异常检测

Even a well-designed feature will eventually face a creative abuse attempt. Logging and monitoring are what let you notice it and respond.

即使是一个设计良好的功能,最终也可能面临创造性滥用尝试。日志记录和监控是让您注意到并做出反应的关键。

What to Log / 需要记录什么

For any operation that dispenses value, changes permissions, or moves money:

对于任何提供价值、更改权限或转移资金的操作:

  • The authenticated user
    中文:认证用户
  • The target resource
    中文:目标资源
  • The action taken
    中文:采取的操作
  • The outcome
    中文:结果
  • Enough request context to reconstruct what happened (IP, user agent, correlation ID)
    中文:足够多的请求上下文来重建发生的事情(IP、用户代理、关联ID)
  • Enough business context to audit (the price computed, the coupon applied, the new state)
    中文:足够的业务上下文以进行审计(计算的价格、应用的优惠券、新的状态)

These logs should be tamper-evident and separate from general application logs.

这些日志应该是可篡改的,并且与通用应用程序日志分开。

What to Alert On / 警报的依据

Rates of the following often precede or accompany abuse:

以下比率通常在滥用之前或伴随滥用发生:

  • Signups from the same IP, device fingerprint, or payment instrument
    中文:来自同一IP、设备指纹或支付工具的注册
  • Repeated failed password-reset requests from the same source
    中文:来自同一来源的重复失败的密码重置请求
  • Unusually high rates of promo redemptions, referral completions, or credit issuances
    中文:促销兑换、推荐完成或信用发行的异常高比率
  • Bursts of requests to a single endpoint from a single user
    中文:单个用户对单个端点的请求突发
  • Workflows that complete in substantially less time than a human would need
    中文:比人类所需时间少得多即可完成的工作流程

You don't need sophisticated machine learning for this. Simple thresholds on per-user, per-IP, and per-device rates catch most automated abuse.

您不需要复杂的机器学习来完成这项工作。简单的每个用户、每个IP和每个设备速率阈值可以捕捉到大多数自动化滥用行为。

Close the Loop / 闭环

When monitoring surfaces a potential abuse, two things should happen: the specific incident gets investigated and resolved, and the control that should have prevented it gets added or tightened. A detection that doesn't lead to a prevention is a detection you'll be making again next month.

当监控表面出现潜在的滥用时,应该发生两件事:具体事件得到调查和解决,以及应该阻止它得到添加或加强的控制。没有导致预防的检测将是您下个月再次进行的检测。

Testing Business Logic / 测试业务逻辑

Automated tests are how business rules stay enforced as the code evolves. Focus test effort on the rules, not the happy path.

自动化测试是业务规则在代码演变过程中保持执行的方式。将测试精力集中在规则上,而不是在顺利路径上。

Test the Rules, Not the Implementation / 测试规则,而不是实现

For each invariant you identified during threat modeling, write a test that would fail if the invariant were violated. A test that says "a user cannot approve their own request" should attempt exactly that and assert the request is rejected. These tests document the rules as much as they verify them.

对于在威胁建模期间确定的每个不变量,编写一个测试,如果违反了不变量,则测试失败。一个说“用户不能批准自己的请求”的测试应该尝试做到这一点,并断言请求被拒绝。这些测试记录了规则,就像它们验证规则一样。

Test Concurrency / 测试并发

If two requests can race, write a test that races them. Most test frameworks have ways to fire concurrent requests and assert the end state is consistent (e.g., balance is never negative, coupon is redeemed exactly once, bonus is granted exactly once across all winners).

如果两个请求可以竞争,编写一个使它们竞争的测试。大多数测试框架都有触发并发请求并断言最终状态一致的方法(例如,余额永远不会为负,优惠券恰好兑换一次,奖金恰好对所有获奖者授予一次)

Test Ordering / 测试顺序

For multi-step workflows, test attempts to skip, repeat, and reorder steps. The expected outcome for each is a rejection, not progress.

对于多步骤工作流程,测试尝试跳过、重复和重新排序步骤。对于每个预期的结果都是拒绝,而不是进展。

Test with an Adversarial Mindset / 以对抗性思维进行测试

Unit tests written by the developer who wrote the feature tend to cover cases the developer was already thinking about. A useful complement is adversarial testing: take the feature specification and write tests for the ways a motivated user would try to misuse it. Often the bug reproduces on the first attempt, because the feature wasn't designed with that misuse in mind.

由编写功能的开发者编写的单元测试往往涵盖了开发者已经考虑到的案例。一个有用的补充是对抗性测试:根据功能规范编写测试,以测试有动机的用户可能会尝试滥用的方式。通常,由于功能没有考虑到这种滥用,因此错误会在第一次尝试时重现。

Common Examples of Business Logic Flaws / 业务逻辑漏洞的常见示例

Concrete examples help developers recognize the pattern in their own code. The list below is not exhaustive.

具体的例子有助于开发者识别他们自己代码中的模式。下面的列表并不全面。

Price and Quantity / 价格和数量

  • Negative quantity in a cart to get a refund against another item's cost.
    中文:购物车中负数量以获取另一项商品的退款。
  • Client-supplied price that the server trusts.
    中文:客户端提供的,服务器信任的价格。
  • Zero-price items that are actually valuable because a "free" flag was intended for a different product.
    中文:实际上有价值的零价格商品,因为“免费”标志原本是为另一款产品设置的。
  • Currency switching mid-transaction where the amount is kept but the currency changes (e.g., from USD to a much weaker unit).
    中文:交易过程中货币切换,金额保持不变但货币改变(例如,从美元切换到更弱的货币单位)。
  • Coupon codes applied multiple times, or applied to products they were not meant for.
    中文:优惠券代码多次应用,或应用到了它们不应应用的商品上。

Workflow / 工作流程

  • Skipping identity verification by calling the post-verification endpoint directly.
    中文:通过直接调用后验证端点来跳过身份验证。
  • Advancing a job application, approval, or KYC workflow past a stage that was supposed to be gated on a reviewer.
    中文:将工作申请、审批或KYC工作流程推进到本应受审查员控制的阶段之后。
  • Triggering "checkout complete" without having actually paid.
    中文:在没有实际付款的情况下触发“结账完成”。
  • Claiming a reward for a task that was never completed.
    中文:声称从未完成的任务的奖励。

Concurrency / 并发性

  • Withdrawing the same balance from two concurrent sessions.
    中文:从两个并发会话中提取相同的余额。
  • Redeeming a single-use voucher from two parallel requests.
    中文:从两个并行请求中兑换单次使用的优惠券。
  • Transferring the same item to two recipients at once.
    中文:一次性将同一物品转给两个收件人。
  • Concurrently updating a counter so that one update is lost.
    中文:并发更新计数器,导致一次更新丢失。

Authorization-in-disguise / 伪装授权

  • Approving your own request because the "not the author" check was forgotten.
    中文:由于忘记执行“不是作者”的检查,批准了自己的请求。
  • Canceling an order after it was supposed to be locked in.
    中文:在订单应该锁定后取消订单。
  • Modifying a shared resource because the "only the owner can modify" rule was never enforced on a specific endpoint.
    中文:由于从未在特定端点上执行“只有所有者才能修改”的规则,修改了共享资源。

Value-dispensing Abuse / 价值分发滥用

  • Claiming a signup bonus multiple times by re-signing up.
    中文:通过重新注册多次来声称注册奖励。
  • Earning referral rewards by referring yourself.
    中文:通过推荐自己来赚取推荐奖励。
  • Stacking promos that were meant to be mutually exclusive.
    中文:叠加那些本应互斥的促销活动。
  • Abusing "refer a friend" mechanics to send spam that looks like it comes from your platform.
    中文:滥用“推荐好友”机制发送看似来自您平台的垃圾邮件。

Summary Checklist / 总结清单

Before shipping any feature that handles money, permissions, or state, walkthrough this list:

在发布任何处理金钱、权限或状态的功能之前,请走查此清单:

  • Are all security-relevant values (prices, permissions, identity, ownership) derived server-side, not accepted from the client?
    中文:所有与安全相关的值(价格、权限、身份、所有权)是否在服务器端生成,而不是从客户端接受?
  • Is every multi-step workflow represented as an explicit state machine in server-side storage, with each transition validated?
    中文:每个多步骤工作流程是否在服务器端存储中作为显式的状态机表示,并且每个转换都经过验证?
  • Is every check-then-act operation atomic (transaction, row lock, or conditional update)?
    中文:每个检查后执行的操作是否是原子的(事务、行锁或条件更新)?
  • Do retries of external actions reuse the same idempotency key within the provider's documented retry window?
    中文:外部操作的重试是否在提供商文档中定义的重试窗口内重用相同的幂等键?
  • Does every value-dispensing feature have a per-action cap, a per-account cap, and a rate limit?
    中文:每个分发值的特性是否具有每个操作的限制、每个账户的限制和速率限制?
  • Are all invariants written down and tested?
    中文:所有不变量都已记录并测试了吗?
  • Is every entry point for a sensitive operation subject to the same business rules?
    中文:每个敏感操作的入口点都受到相同业务规则的约束吗?
  • Does logging capture enough context to reconstruct abuse after the fact, and do alerts fire on anomalous rates?
    中文:日志是否捕获了足够的信息以便事后重建滥用情况,并且是否在异常速率上触发警报?
  • Have you considered the dishonest-user perspective, not just the attacker-with-exploit perspective?
    中文:您是否考虑了不诚实用户的角度,而不仅仅是具有利用能力的攻击者的角度?

References / 参考