Skip to content
Case study

Record-level authorization for a Base44 equipment-rental marketplace

Page-level permissions were correct. Record-level permissions did not exist. Sequential identifiers meant any signed-in renter could read - and cancel - someone else's booking.Composite engagement. Details anonymised; figures are representative.

Headline result

6 → 0

endpoints returning other users' records

Platform
Base44
Industry
Marketplace · Equipment rental
Duration
4 weeks
Findings
2 critical, 2 high, 2 medium
The situation

What we walked into

A two-sided marketplace for construction and event equipment, built in Base44 over about ten weeks. Roughly 900 renters and 140 equipment owners, with bookings running through the platform and payouts to owners twice a month.

The founders came to us about cost. They were on a plan that was starting to sting and wanted to know whether moving off the platform would pay for itself.

The app asked the right first question - is this user a renter or an owner?

Engagement

Every engagement opens with a free consultation and a scoped audit. This one drew on the following:

Composite engagement. Details anonymised; figures are representative.

Production check

What we found

Severity is about consequence, not effort. A critical finding is one where a stranger can read data, move money, or destroy state.

2 Critical2 High2 Medium
  • Critical

    Record ownership never checked on read or write

    Six endpoints loaded a record by the identifier in the request and returned it without asking who was asking.

  • Critical

    Owner payout details reachable by any account

    The payout settings record was fetched by owner id with no membership check.

  • High

    Authorization written in the interface, not the data layer

    Role checks lived in page-level conditions. The data operations underneath accepted any authenticated caller, so the API surface effectively had no rules.

  • High

    No audit trail on money-moving actions

    Cancellations, refunds, and payout changes wrote no record of who performed them.

  • Medium

    Sequential identifiers exposed in URLs

    Beyond making the authorization problem trivially exploitable, auto-incrementing ids leak business volume.

  • Medium

    Browse page loaded every listing

    The directory fetched all active listings with all fields and filtered in the browser.

Production ready

The work we did

Delivered over 4 weeks, on branches, alongside a founder who kept shipping product the whole time.

  1. 01

    Wrote the authorization model down before touching code: eleven rules covering who may read, create, modify, and cancel each entity, reviewed line by line with the founders.

  2. 02

    Moved every rule into the data layer, so the check happens where the record is fetched rather than where a page decides what to render.

  3. 03

    Replaced sequential integers in public URLs with opaque identifiers, kept the internal keys, and added a lookup so existing links and bookmarked confirmations keep working.

  4. 04

    Built an authorization test suite: for each of the eleven rules, one test that the permitted role succeeds and one test per other role that it receives a 403.

  5. 05

    Added an append-only audit log for cancellations, refunds, and payout changes, recording the actor, the target record, and the previous value.

  6. 06

    Reviewed six months of access logs with the founders to establish whether the gaps had been exploited, and helped them draft the note they sent to affected owners.

  7. 07

    Added server-side pagination, indexed filters, and field selection to the browse and search pages.

  8. 08

    Wired up structured logging and error tracking, so the next incident has a trail instead of a reconstruction.

Measured

Before and after

The same metrics, measured the same way on both sides of the work. Where a number got worse, it is in the table.

  • Endpoints returning other users' records

    60

    Confirmed by replaying every request with a token from an unrelated account.

  • Authorization rules covered by tests

    0 of 1111 of 11

    Each rule has a positive and a negative case; CI blocks a merge that breaks one.

  • Browse page p95

    3.6 s620 ms

    2,400 listings, server-side pagination, indexed filters.

  • Money-moving actions with an audit trail

    NoneAll

    Append-only log with actor, target, and previous value.

  • Time to answer “who changed this?”

    Not answerableUnder a minute

    The question that turned a routine security review into a two-day exercise.

  • Monthly infrastructure cost

    $180/mo$310/mo

    The platform plan did not change. The increase is observability, log retention, and a staging environment.

Composite engagement. Details anonymised; figures are representative. Figures are rounded from what we measured across the engagements this study is drawn from.

What it runs on now

The builder stayed in the picture where it earned its place. Nothing here was rewritten for the sake of rewriting it.

  • Base44
  • PostgreSQL
  • Node.js service layer
  • Playwright
  • GitHub Actions
  • Sentry
  • Structured log retention
We asked for a cost review. The first thing we were shown was our own booking table, opened from an account that had no business seeing it.
Co-founder, rental marketplaceParaphrased from a composite engagement and attributed by role only. No individual or company is identified.

Same builder, same symptoms?

Send us a repo link and a paragraph about what it does. The audit is free and the report is yours either way.

Book a Free Call