Mahamudul Hasan Rubel
HomeBlogCoursesAboutProjectsSkillsExperiencePhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • Blog
  • Courses
  • About
  • Projects
  • Skills
  • Experience
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

Subscribe to the newsletter

Get new articles and course lessons delivered to your inbox. No spam, unsubscribe anytime.

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
SecurityJune 26, 20264 min read

Business logic security: Preventing state manipulation in workflows

Master business logic security to prevent state manipulation in multi-step workflows. Learn how to enforce server-side validation and protect critical paths.

application securitybusiness logicstate managementbackend developmentweb securitySecurityWebBackend

Last month, our team spent about three days debugging a payment bypass that had nothing to do with broken crypto or SQL injection. A user discovered that by skipping the "Shipping Address" step and jumping directly to the "Payment Confirmation" API endpoint, they could place orders without providing a delivery address. Our backend assumed that if the user reached the final endpoint, the previous steps had already occurred.

It was a classic case of state manipulation. We had robust input validation, but we lacked meaningful business logic security.

Why Input Validation Isn't Enough

Most developers treat security as a boundary problem. We sanitize strings, validate email formats, and use DTOs to prevent mass assignment vulnerabilities. However, even if your inputs are clean, your state transitions might be wide open.

When you manage multi-step workflows, the sequence of operations is just as important as the data itself. If your application trusts the client to dictate the current step in a process, you’ve already lost. An attacker doesn't need to inject malicious code to compromise your system; they just need to change the flow.

The Danger of Client-Side State

We initially tried storing the workflow progress in a hidden field on the frontend. It looked something like this:

JSON
// Frontend-controlled state
{
  "order_id": "123",
  "current_step": "payment",
  "total_price": 49.99
}

This is a recipe for disaster. An attacker can simply change current_step or, even worse, modify total_price before sending the request. By the time the server receives this payload, it’s too late to verify if the price was actually $49.99 or if the user manipulated it to $0.01.

To implement effective business logic security, the server must be the sole source of truth for the session state.

Enforcing Secure Workflows Server-Side

To fix our workflow, we moved the state management into our Redis-backed session store. Instead of relying on the client to tell us where they are, we calculate the allowed transitions on the server.

Here is the pattern we adopted for our multi-step checkout:

  1. Define a State Machine: Create a simple map of valid transitions.
  2. Validate on Every Request: Check the current state in the session against the requested action.
  3. Atomic Updates: Update the session state only after the logic for the current step succeeds.

Implementing a Transition Guard

Instead of checking if an endpoint exists, check if the transition is allowed from the current state.

JAVASCRIPT
// Example check in a Node.js Express controller
const ALLOWED_TRANSITIONS = {
  CE9178">'CART': [CE9178">'SHIPPING'],
  CE9178">'SHIPPING': [CE9178">'PAYMENT'],
  CE9178">'PAYMENT': [CE9178">'COMPLETE']
};

function validateTransition(currentStep, nextStep) {
  if (!ALLOWED_TRANSITIONS[currentStep].includes(nextStep)) {
    throw new Error("Invalid workflow transition attempted.");
  }
}

By using this approach, we effectively mitigate state manipulation. If a user tries to POST to /payment while their session state is still CART, the server rejects the request immediately.

Beyond Simple Transitions

If you are building complex systems, consider these additional layers:

  • Server-Side Totals: Never accept the final price from the client. Fetch the price from the database using the order_id stored in the session.
  • Time-Bound Tokens: If a workflow is sensitive, use short-lived, server-generated tokens for each step.
  • Audit Logging: Log every state change. If an account suddenly jumps from Registration to AdminAccess without the intermediate EmailVerification step, you know exactly when the breach attempt occurred.

We've found that applying these principles alongside business logic vulnerabilities: securing your multi-step checkout workflow significantly hardens our applications.

What I'd Do Differently

Looking back, I wish we had implemented a formal state machine library from day one. Hand-rolling these checks works for simple flows, but as our application grew to include complex features like recurring subscriptions and dynamic discounts, our manual if/else logic became brittle.

We also struggled with testing. It's difficult to write unit tests for state if you aren't mocking the session store correctly. Next time, I’ll prioritize writing integration tests that specifically attempt to "skip" steps in the flow to ensure the server rejects them.

Security isn't just about sanitizing inputs. It’s about building a system that enforces the rules of your business, regardless of what the client tries to send you. Keep your state on the server, enforce your transitions, and don't trust the browser to do your job for you.

Back to Blog

Similar Posts

SecurityJune 25, 20264 min read

Parameter pollution: Securing Express and Laravel Request Parsing

Parameter pollution can lead to serious logic flaws in your web apps. Learn how to secure your Express and Laravel request parsing against manipulation.

Read more
SecurityJune 28, 20264 min read

Cache Poisoning Prevention: Securing Against X-Forwarded-Host Manipulation

Cache poisoning happens when malicious headers trick your proxy. Learn how to secure your apps against X-Forwarded-Host header manipulation and proxy risks.

Read more
SecurityJune 27, 20264 min read

LDAP Injection Prevention: Secure Directory Queries in Node.js & PHP

LDAP injection can expose your entire directory service. Learn how to implement secure coding and input sanitization in Node.js and PHP to block attacks.

Read more