Back to Feed
article 3m read

Company picker added to the backup page, and it still backs up every company

The scheduling app's backup page grew a required company picker, per-company folders and a path check in one day, but the step that makes the dump never got the company, so every file holds every customer. Restore has the same problem in reverse.

Article DepthPractice

Flow

Checking a per-company backup

  1. 1Trace the company ID

    See if it reaches the step that reads the data.

  2. 2Test with two companies

    Give each a marker row and look for the wrong one in the file.

  3. 3Pick an honest shape

    Name it a full backup, or filter every table by company.

  4. 4Move the files

    Keep backups out of the public web folder.

  5. 5Fix the secrets

    Password off the command line, login token out of the link.

The backup page makes you pick a company first. Then it backs up every company.

Several businesses use my scheduling app, and they all share one database. The admin side has a backup page. You press a button and get an encrypted copy of the database to download. In one day of changes, that page grew a lot. You had to pick a company. The pick went into the audit log. Files went into a folder per company. Downloads were locked to that folder. A typed password took over from the server's key.

Each change made the page look more like a per-company backup. None of them changed what went into the file.

The function that never sees the company

I traced the company ID from the request. It gets checked, logged, and used to pick the folder. Then the code calls the function that makes the backup, and the company isn't passed in. That function just dumps the whole database.

dump_database(out, password)   # no company here

So a file filed under one company holds every company. Anyone who downloads it for one customer has everyone's schedules and staff records. The audit log still says it was one company's backup.

Restore has the same problem the other way. It loads the whole dump back into the live database. Undoing one company's mistake undoes everyone's work since that backup.

Why the path check fooled me

The folder check is real and careful. It makes sure you get a file from the right folder. It says nothing about what's inside the file. That care made the whole thing look safe.

The other stuff the same day dragged in

The backups now sit inside the public web folder and stay there. Nothing in the day's changes blocks that folder from the web, so I'm assuming anyone with the URL can fetch them. Only the password protects them. The password also shows up on the command line, where other programs on the server can see it. And the download link puts the login token in the URL, where logs can catch it.

None of those changes added a test. A test with two companies, each with a marker row, would have caught this right away. Back up the first one, then make sure the second marker isn't in the file.

Full-system backup, or a real export

I have two honest choices. I can keep it as a full backup and say so: no company picker, a clear name, only a few people allowed, and storage outside the web folder. Or I can build a real per-company export that filters every table by company. That second one is a lot of work, and it's hard to prove I got every table.

I'm leaning toward the full backup, named for what it is. A separate per-company export can come later, and it won't be something you restore from.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward