Storing scanned documents responsibly means every citizenship scan is encrypted at rest, reachable only through authenticated, logged links that expire in minutes, and deleted on a published schedule. It should never sit in a web-browsable folder. One leaked citizenship file is a complete identity-theft kit, so the storage design matters more than the scanner.
Key Takeaways
Storing scanned documents safely comes down to four controls: encryption at rest, authenticated access through short-lived signed URLs, an audit log of every view, and scheduled deletion that includes backups. Miss any one and the others only slow an attacker down. The takeaways below compress the practice.
- Keep scans outside the web root, or in a private object bucket. Nothing under
/public, nothing browsable. - Encrypt at rest (server-side AES-256, or LUKS/BitLocker on volumes) and in transit (TLS only).
- Serve files through your application with signed URLs that expire in minutes, and log every view.
- Rename uploads to random names, check the real file type server-side, virus-scan before storing.
- Back up encrypted, under separate credentials, and test a restore every quarter.
- Define retention per record type; deletion must reach backups and old versions, or it didn't happen.
What does storing scanned citizenship documents responsibly involve?
It means treating each scan as a regulated record rather than a photo. The file must be encrypted at rest, moved over TLS, readable only through your application's authorisation checks, logged on every view, and destroyed when its retention period ends. Nepal's Individual Privacy Act 2075 and sector know-your-customer (KYC) rules make this a legal duty.
A citizenship certificate carries a photo, date of birth, address, parents' names and a unique number — enough to open a SIM contract or a loan in someone else's name. One file therefore has the blast radius of a whole identity. In the incidents we get called to, the cause is rarely a skilled attacker. It's an uploads folder readable by the whole internet, a shared-drive password that left with a former employee, or a laptop holding thousands of scans. Design as though the file will leak, then make leaking hard.
When should you keep a scan — and when should you delete it?
Collect a citizenship scan only when a law, regulator or contract genuinely requires it, and delete it when that requirement ends. Keeping scans "just in case" multiplies your liability every year. Write a retention period per record type, get sign-off from whoever owns compliance, and let a scheduled job do the deleting.
Start with minimisation. If you verify identity once, the citizenship number plus the name is usually enough — you don't need a permanent copy of the scan. Where you do need the image — KYC files at a bank or cooperative, employee records, permit paperwork — write the retention period down. Financial institutions answer to Nepal Rastra Bank's KYC directions; employers answer to labour and tax record rules. Confirm the exact periods with your own legal adviser rather than copying a figure from a blog post, this one included.
Where should scanned documents physically live?
Put scans in a private object store such as Amazon S3, DigitalOcean Spaces or Cloudflare R2, or on a disk volume outside the web root. Never inside a public uploads folder, never as database blobs. Object storage gives you encryption at rest, signed URLs and versioning without running extra servers.
Object storage wins because the hard parts are done for you: server-side encryption (read Amazon's S3 server-side encryption guidance), links that expire, access policies, versioning. A private volume on your own server also works, provided files sit outside the web root and reach the browser only through your application — on NGINX, an internal location served with X-Accel-Redirect does exactly that. The database is the wrong home: scans stored as bytea bloat every backup and push whole files through the app server's memory on each download, as the PostgreSQL docs make plain. If you're weighing where the portal itself should run, our shared hosting, VPS or cloud comparison covers that layer.
How do you set up secure storage step by step?
Set the workflow up in this order: decide what you must keep, choose private storage, harden it, fix the upload path, then layer on access control, logging, backups and retention. Verify each stage before moving on — a private bucket behind a public backup is still a leak.
- Inventory and minimise. Find every place scans land today — form inboxes, WhatsApp chats, desktop folders. Decide per record type whether you need the image or just the number.
- Choose the store. A private bucket (S3, Spaces, R2) or a volume outside the web root — in Laravel,
storage/apprather thanpublic/. - Encrypt and lock it down. Turn on server-side encryption, then block anonymous access. The command below changes who can reach the bucket — it shuts out all public access, so confirm nothing public depends on it first:
aws s3api put-public-access-block \
--bucket mycompany-docs \
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true - Fix the upload path. Allow-list the real file type using server-side checks (PHP's
finfo, orfileon the shell), cap the size, rename to random names, and run ClamAV'sclamscanbefore storing. Whether intake is Laravel or WordPress, the rules are the same — and the ways users get uploads wrong deserve their own checklist. - Serve through the app only. Generate signed URLs that expire in minutes. Staff never receive a permanent link.
- Log every view. User, record, file, IP address, timestamp — written somewhere staff cannot edit.
- Back up encrypted under credentials that cannot touch production, then prove it: restore to a scratch machine and run
sha256sum -cagainst the manifest from upload day. - Automate retention. A scheduled job deletes expired records, old object versions and backup copies, writing each destruction to the audit log. Deletion is irreversible — dry-run it against a printed list first, and have the retention dates reviewed before the first run.
Which configuration settings matter most?
Five settings carry most of the risk: encryption at rest, TLS-only transport, signed-URL lifetime, upload validation and audit logging. Get these right and everything else is refinement. The table lists each setting, why it matters, and the default we consider sane for a small team.
| Setting | Why it matters | Sane default |
|---|---|---|
| Encryption at rest | A stolen disk or snapshot is worthless without the key | Server-side AES-256; LUKS or BitLocker on volumes |
| Transport | Scans cross the internet on every upload and view | TLS 1.2 or newer, nothing else |
| Signed URL lifetime | A leaked link should die before it spreads | 5–15 minutes |
| Upload validation | Browsers lie about file types; shell.php.jpg is a classic | Server-side MIME check, size cap, random names |
| Audit logging | You cannot prove careful handling without a record | Log every view: user, record, IP, time |
Storage itself is cheap — scans are small, so ten thousand citizenship files is a few gigabytes. The real running cost is engineer time: patching, watching logs, the quarterly restore drill, keeping the retention job honest. That ongoing discipline is exactly what our website maintenance work covers for client systems.
How do you verify the storage is actually safe?
Verify from the outside, the way an attacker would. Open a file URL in an incognito window, request a record you are not authorised to see, and list the bucket anonymously. Every attempt should fail with a 403, and every failure should appear in your logs.
For the bucket, try anonymous listing:
aws s3 ls s3://mycompany-docs --no-sign-request It should answer AccessDenied. Logged in as staff member A, try to open member B's record by editing the URL — that insecure-direct-object-reference test should return 403, not the file. Also set Content-Disposition: attachment so a scan downloads instead of rendering in the browser. Then prove the backups: restore last night's copy to a scratch machine, verify checksums, open two samples. A backup you haven't restored is a hope, not a backup.
What breaks, and what do you check first?
Most incidents are boring: a folder was browsable, a backup was public, a former employee's login still worked. Check exposure first, then access logs, then the application itself. The failure pattern usually tells you which layer broke before you open a single config file.
- A scan opens without login. Check placement: the file is either under the web root or in a bucket without public access blocked. Fix the storage, then work out exposure time from access logs and a
site:search on your domain. - Downloads crash with "Allowed memory size exhausted". Your scans live in the database. Move them to object storage and stream through the app.
- Uploads fail at random. Compare PHP's
upload_max_filesizepost_max_size and the web server'sclient_max_body_size— whichever limit is smallest wins. Mismatched limits produce silent truncation or a bare 413 with no hint on the client side. - A deleted record's scan is still online. Versioning kept the old object, or last night's backup still carries it. Purge object versions explicitly and confirm the retention job walks every copy, including the archive.
What does this look like for a real organisation?
Picture a Kathmandu cooperative that onboards 200 members a month and needs each citizenship scan in the loan file. The sound design is small: a private bucket with encryption on, a Laravel portal serving five-minute signed links, nightly encrypted backups, and a retention job set to the period their regulator expects.
The mistake version looks seductively similar — same portal, but scans dropped into a web-browsable uploads folder because it shipped faster. It passes the demo and leaks two years later when someone shares a folder link. And where the law allows it, the simplest alternative still stands: collect the citizenship number only and keep paper originals in a locked cabinet. Digital copies earn their complexity only when staff genuinely need remote access to the images.
In short
Storing scanned documents responsibly comes down to six habits: keep files outside any public path, encrypt at rest and in transit, serve them through short-lived signed links, log every view, back up under separate credentials, and delete on a schedule that reaches versions and backups. Do those consistently and a leak stops being a question of when.
People also search for
These are the questions our clients ask most often once a document-storage plan is on the table. Each links to a fuller guide on our blog, from why users submit the wrong file to how a build is actually quoted. Follow whichever matches your current project stage.
- Why users upload the wrong file format or an unreadable scan
- What a proper website documentation handover should include
- Shared hosting vs VPS vs cloud for a document portal
- Custom software vs off-the-shelf for KYC-style workflows
- How a web development quote is actually broken down
- More guides on building and running document systems
If your organisation is building a member portal, HR system or KYC intake that collects citizenship scans, our team can help you get the storage right from day one — private bucket, encryption, signed links, tested backups, retention. See what we build and run or contact us for a review of how your documents are handled today.












0 comments
Be the first to share your thoughts.
Leave a comment
Replying to — cancel