Flow
Checking a per-company backup
1Trace the company ID
See if it reaches the step that reads the data.
2Test with two companies
Give each a marker row and look for the wrong one in the file.
3Pick an honest shape
Name it a full backup, or filter every table by company.
4Move the files
Keep backups out of the public web folder.
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.