In Episode 8 of the Coinbase Security Series, Hacken’s Senior Smart Contract Auditor, Olesia Bilenka, breaks down one of the most overlooked realities in the industry:
Smart contract security alone is not enough — many exploits often arise in the Web2 layer that surrounds Web3 systems.
Web3 security discussions usually focus on smart contracts. Audits, tooling, and risk analysis often begin and end with on-chain logic. But real systems are hybrids: every decentralized app relies on a substantial Web2 layer that manages authentication, admin interfaces, business logic, metadata, asset indexing, and user actions. When this layer is weak or unsynchronized with the blockchain, even a fully secure contract can’t prevent serious exploits.
Where Web2 Meets the Onchain World
Most onchain apps aren’t just smart contracts. They’re hybrids. The contract handles the trust, but everything surrounding it — the login flow, the database, the game logic, and the admin tools — still resides in Web2.
That layer runs a lot of the experience, including:
- authentication
- admin panels
- business logic
- asset indexing and metadata
- gameplay systems
- user state
When any part of it drifts from the chain or trusts whatever the client sends, attackers find an opening. The mismatch becomes the vulnerability.

Problem Pattern #1: Split-Call Workflows
A split-call workflow appears when a single business action is divided across two separate calls — for example, minting an NFT on-chain and then registering it in the backend through an API. When these steps are not tightly synchronized, a gap opens where the on-chain and off-chain states can diverge. That divergence becomes a window for exploitation.

Real Attack Scenario
In a Web3 game, the NFT import flow followed this two-step design. The user minted an NFT on-chain, and then the client called /register to sync it into the Web2 backend. The backend trusted the owner value provided by the client and did not verify it against the chain. This created a race window between the mint and the registration.
An attacker could take advantage of this gap:
- Mint the NFT
- Sell it on-chain immediately
- Complete the /register call before the backend had synchronized
The backend accepted the attacker’s address as the owner and stored it in its database, even though the blockchain already reflected a different owner. This was possible because the backend trusted client input, performed no on-chain revalidation, had no locking or synchronization, and implemented registration logic entirely in Web2.
Impact
This small timing gap had serious consequences:
- Unauthorized NFT imports
- Real owners are blocked from using their own asset
- Attackers obtaining bonuses or in-game advantages
- Divergent and inconsistent product state
- Increased support overhead and broken UX
The smart contract was secure, but the system around it was not.
Fix: Event-Driven Worker
The remediation was to remove trust in client-provided ownership and move registration into a dedicated event-driven worker. Instead of letting the client submit the owner:
- The worker listens to mint events
- Waits for block confirmations
- Reads the actual owner with ownerOf(tokenId)
- Updates the backend through a secure internal endpoint
This closes the race window entirely and ensures that ownership flows from the blockchain, not from the frontend. Off-chain updates should always be triggered by on-chain events — not by client actions.
Problem Pattern #2: Multi-Source State Drift
Multi-source state drift occurs when an application tracks asset ownership or other critical data in two places — on-chain and in an off-chain database. If synchronization between these sources is delayed or unreliable, the backend may end up relying on stale information. Even valid user actions can then produce incorrect behavior, because the system no longer reflects the real blockchain state.

Real Scenario
In one audited system, the backend stored NFT owners in its database and checked permissions against this stored value. Ownership on-chain could change instantly, but the backend’s cached state did not update until an event listener eventually processed it.
This created a situation where a user could transfer their NFT on-chain, but still appear as the owner in the backend. For example:
- User A transfers an NFT to User B
- On-chain, the owner is now B
- The backend has not yet updated its cache
- User A can still perform off-chain actions tied to ownership
- User B is blocked from performing legitimate actions
No attacker was required. The flaw was purely architectural: the system trusted stale off-chain data instead of the blockchain.
Impact
Drift leads to “ghost ownership,” incorrect permissions, blocked legitimate users, broken gameplay or business logic, and manual cleanup. These are the kinds of issues that often pass functional testing but create severe problems in production.
Fix: Event-Driven Sync + Real-Time Revalidation
The remediation mirrors the principles used in Pattern 1. First, the system must update its database using a trusted worker:
- Listen to Transfer events
- Wait for confirmations
- Fetch the verified owner with ownerOf()
- Update the internal state
However, event-driven sync alone is not enough. The backend must also re-check ownership on-chain during every sensitive action. Even if the database falls behind, the system can still prevent incorrect behavior by validating ownership directly on the blockchain at the moment of use.
Together, these steps eliminate stale permissions and ensure the database cannot drift silently from the contract state. The blockchain becomes the single source of truth, and the backend merely mirrors it.
Problem Pattern #3: Business Logic Enforcement Gap
This pattern arises when critical business rules (e.g., price caps, mint limits, quotas, or whitelists) are enforced only in Web2. The frontend and backend may apply these rules consistently, but if the smart contract does not enforce the same constraints, attackers can simply bypass the Web2 layer and interact directly with the contract.

Real Scenario
A typical example involves mint limits. The backend enforced a maximum of 100 items per user, and the UI displayed the same limit. But the contract’s own limit was set to 1000. The backend assumed it was the authoritative gatekeeper and that everyone would interact through the frontend.
Attackers ignored the UI entirely and called the contract directly, minting far beyond what the backend allowed. Because the contract accepted the transaction, supply constraints broke, business invariants failed, and other users experienced errors or blocked transactions.
Impact
The backend believed rules were enforced, but the contract followed different logic. This discrepancy allowed:
- Direct bypass of Web2 controls
- Manipulation of supply or other critical values
- Broken game or economic rules
- Stuck or corrupted listings
- Mismatched assumptions between layers
Any rule enforced only off-chain is effectively optional for attackers.
Fix: On-Chain Enforcement or Cryptographic Binding
To prevent this, critical business rules must be enforced where they cannot be bypassed: on-chain. Limits and invariants should be encoded directly in the contract. If a rule must remain off-chain for performance or business reasons, it should be expressed through a signed message (e.g., EIP-712) that the contract verifies.
In other words:
- On-chain logic should reflect the same constraints as the backend
- Off-chain logic must be cryptographically tied to contract behavior
- Backend assumptions must align exactly with on-chain enforcement
When Web2 and Web3 disagree, attackers target the weaker layer, and it is almost always Web2.
Builders’ Checklist for Securing Web2 Components
Pattern 1: Split-Call Workflows
- Reduce multi-step flows wherever possible.
- Trigger off-chain updates from on-chain events, not from the frontend.
- Revalidate on-chain state for every sensitive backend action.
- Block actions until the system has fully synchronized.
Pattern 2: Multi-Source State Drift
- Treat the blockchain as the single source of truth.
- Use trusted workers to sync state, not client-side callbacks.
- Perform real-time on-chain rechecks during sensitive operations.
- Regularly reconcile your database with the on-chain state.
Pattern 3: Business Logic Enforcement Gaps
- Enforce critical business rules directly on-chain.
- When logic must stay off-chain, use EIP-712 signatures.
- Ensure backend and contract logic share the same data model and limits.
- Continuously test for drift between Web2 and Web3 enforcement paths.
Conclusions & Key Takeaways
Every onchain product still leans on Web2 rails: the APIs, the databases, the logic that keeps everything moving. That’s the layer attackers hit first. The biggest vulnerabilities don’t appear in the contract — they appear in the space between your backend, your frontend, and the chain.
Locking down that layer is how you ship an app that feels smooth, trustworthy, and ready for real users.

This article is an adaptation of insights shared during Coinbase’s Web3 Security Session (Episode 8).
The session goes deeper into how these hybrid vulnerabilities show up in real products, how attackers take advantage of them, and how teams fixed the issues in production. It covers why Web2 often gets overlooked in onchain security, the failure patterns that keep repeating across audits, real examples from a Web3 game, and practical steps to strengthen both layers of a hybrid app.
If you’re building or securing anything that touches both Web2 and the chain, the full session is worth watching.
You can rewatch it here: https://x.com/i/broadcasts/1lPKqvAPrqeGb



