Loading

Public & Private Paths

Where uploaded files live, the difference the location makes, and serving private files with permissions applied.

Public and Private

Uploads go to one of two configured locations, and the choice decides whether the file has access control.

Where to find it

Architect Panel → Configuration:

  • Site Settings — the File Uploads and Audio/Video Conversion groups

Architect Panel → Data:

  • File Stores — the stores themselves, and View File Store on a row
  • Large Uploads — in-progress and stalled upload sessions
  • File Import Routines — watched sources and their mappings

The difference

  • Public — served directly by the web server. Anybody with the address has the file.
  • Private — outside the served directories, reachable only through the platform, which checks permissions first.

“Unguessable” is not access control

The reasoning that puts sensitive files in public directories. A long random filename is not a permission — it is shared in an e-mail, appears in a browser history, is indexed if a link exists anywhere, and never expires.

Files that should be restricted go in the private location. There is no version of this argument that works.

The rule

Private unless the file is genuinely intended for anybody. Logos, public documents, marketing images and site assets are public. Everything a user uploaded, everything attached to a record, and everything about a person is private.

Public is faster

Which is the legitimate reason for it. A file served directly does not involve the application, so site assets belong there for performance as well as simplicity.

Check what is where

Public directories accumulate things nobody meant to publish — a file placed there during setup, an export saved for convenience, an attachment written to the wrong location.

Listing what is in your public directory is a short and occasionally alarming exercise.

Moving a file changes its address

So anything already linked breaks. Decide before files are stored rather than after, and remember that if you move something from public to private, existing links stop working — which is the point, but somebody will report it as a fault.

Test as somebody with no access

Take the address of a private file and open it in a private window with no session. It should refuse. That is a two-minute test and it is the only proof.

Backups and copies follow the same rule

A private file copied into a public directory for a migration, or an export written somewhere convenient, has left its protection behind. Those copies are where disclosures come from.

Worked example

An organisation keeps only site assets in its public directory and everything else private. A review of the public directory found three exports left there during a migration two years earlier, one containing customer names, which were removed.

Recommendations

  • Private unless genuinely public.
  • Never rely on unguessable names.
  • List the public directory periodically.
  • Test a private file from a session with no access.

Serving Private Files

A private file is not served by the web server. It is fetched by the platform, which checks whether the requester may have it.

Where to find it

Architect Panel → Data:

  • File Stores — the stores themselves, and View File Store on a row
  • Large Uploads — in-progress and stalled upload sessions
  • File Import Routines — watched sources and their mappings

Architect Panel → Security:

  • Permissions — what the check consults

Architect Panel → Activity:

  • Error Log — refused requests

What the serving layer does

  1. Establishes who is asking.
  2. Establishes which file, and which record it belongs to.
  3. Checks whether that person may see that record.
  4. Serves the file, or refuses.

That third step is the whole feature. Without it you have a private directory served publicly by a different route.

Permission on the record, not the file

The right question is whether the person may see the thing the file is attached to. A file is not usually a separate object with its own audience; it belongs to a case, an order, a person.

Checking only that somebody is signed in is the commonest weakness — it turns "private" into "private from the public internet", which is a much weaker claim.

Refuse the same way every time

A refusal that differs depending on whether the file exists tells an attacker which record identifiers are real. The same response for "no such file" and "not allowed" gives nothing away.

Never accept a path from the request

A file should be identified by its record, not by a location the requester supplies. Accepting a path is how a request for something outside the intended directory succeeds.

Set the response headers deliberately

The content type and whether the file displays or downloads. Serving user-uploaded content in a way a browser will render is a way to make an upload behave like part of your site.

For anything user-supplied, prefer download over display.

Consider logging access

For sensitive documents, who fetched what and when is worth having. It is the same reasoning as a read log on a record, and files are frequently the most sensitive part of one.

Watch for enumeration

A single user requesting many files in sequence is either a legitimate bulk export or somebody working through identifiers. Either way it is worth noticing.

Test it properly

Take a real file address, and try it: signed out, signed in as somebody without access, and signed in as somebody with it. Only the third should succeed.

Do this after any permission change, because file access follows record access and record access changes.

Worked example

An organisation tests private file access with three accounts after every permission change. One test found a file reachable by any signed-in user because the check confirmed authentication rather than record access — a distinction that only appears when you test with the second account.

Recommendations

  • Check record access, not merely that somebody is signed in.
  • Identify files by record, never by a supplied path.
  • Refuse identically whether or not the file exists.
  • Test with three accounts after permission changes.