diff --git a/content/guides/02.content/4.import-export.md b/content/guides/02.content/4.import-export.md index c5e2ae03..ccbfd351 100644 --- a/content/guides/02.content/4.import-export.md +++ b/content/guides/02.content/4.import-export.md @@ -50,9 +50,24 @@ they are exported. | **Format** | Choose to export items as CSV, JSON, XML, or YAML. | | **Limit** | Set the maximum number of items to be exported. | | **Export Location** | Download the export file directly to your machine or to the file library. | -| **Folder** | Choose the Folder to download to (if export location is the folder library). | +| **Folder** | Choose the folder to save to (if export location is the File Library). Defaults to the **Default Exports Folder**, or the **Default Folder** if none is set. | | **Sort Field** | Choose field to sort items by. | | **Sort Direction** | Choose to sort items in ascending or descending order. | | **Full-Text Search** | Limit exported Items to ones which matched as search results. | | **Filter** | Limit exported items with a filter. | | **Fields** | Add, remove, and re-order the item fields that will be exported. | + +### Exports Saved to the File Library + +Exports of 2,500 or more items, or more items than [`QUERY_LIMIT_MAX`](/configuration/security-limits) allows, are processed in batches and always saved to the File Library. + +Exports saved to the File Library without a folder go to the **Default Exports Folder** set in **Settings → Files & Storage**. If no Default Exports Folder is set, they go to the **Default Folder**, then the root of the File Library. + +The export menu only shows options the current user has permission to use: + +- The **File Library** location is only available to users with create access on `directus_files`. If an export is too large to download and the user cannot save it to the File Library, the export is blocked. +- The **Folder** picker is only available to users with read access on `directus_folders`. Other users see a notice stating where the file will be saved. + +::callout{icon="i-lucide-triangle-alert" color="warning"} +Exported files are regular files. Anyone with read access to them, including unauthenticated users if a policy attached to the Public role allows it, can view and download the exported data. Set the Default Exports Folder to a folder no public policy can read. See [Scope Public File Access to Specific Folders](/guides/security/best-practices#scope-public-file-access-to-specific-folders). +:: diff --git a/content/guides/03.auth/2.access-control.md b/content/guides/03.auth/2.access-control.md index e61d53e6..1168ba94 100644 --- a/content/guides/03.auth/2.access-control.md +++ b/content/guides/03.auth/2.access-control.md @@ -81,7 +81,7 @@ All public permissions are **off by default**. It is up to administrators to re- ::callout{icon="i-lucide-triangle-alert" color="warning"} Granting collection-level read access to the public role exposes **all items** in that collection to anyone, including unauthenticated users and bots. If that collection contains a mix of published and unpublished content, all of it will be accessible via the API unless you configure additional restrictions.

-Review [Minimize the Public Role](/guides/security/best-practices#minimize-the-public-role) in Security Best Practices before enabling public access. +Review [Secure the Public Role](/guides/security/best-practices#secure-the-public-role) in Security Best Practices before enabling public access. :: ## Statuses diff --git a/content/guides/05.files/5.transform.md b/content/guides/05.files/5.transform.md index fc020333..9519433e 100644 --- a/content/guides/05.files/5.transform.md +++ b/content/guides/05.files/5.transform.md @@ -169,6 +169,7 @@ The following options are available: - **Allowed Transformations**: for enabling, disabling, or limiting image transformations. - **Default Folder**: sets the default folder where new assets are added. This does not affect existing files. Be aware that fields may override this value. +- **Default Exports Folder**: sets the default folder where exports saved to the File Library are added. - **Transformation Presets**: sets a specific image transformation configuration to simplify requests or limit usage. - **Key**: sets unique identifier allowing faster and easier image transformation requests. - **Fit**: contain _(keeps aspect ratio)_, Cover _(exact size)_, Fit Inside, or Fit Outside. diff --git a/content/guides/14.security/1.best-practices.md b/content/guides/14.security/1.best-practices.md index 48733348..4b6db9f0 100644 --- a/content/guides/14.security/1.best-practices.md +++ b/content/guides/14.security/1.best-practices.md @@ -95,14 +95,81 @@ Grant all access on `directus_shares` to administrators only. ## Access Control -### Minimize the Public Role +### Secure the Public Role -The public role applies to every unauthenticated request. Granting read access on a collection to the public role exposes **all items** in that collection to anyone on the internet, including items you might assume are filtered by your frontend. +The **Public role** applies to every unauthenticated request made to your project. Every policy attached to it, referred to below as a public policy, grants its permissions to anyone on the internet, including visitors, bots, and scripts. Treat every public permission as world-readable, or world-writable for create, update, and delete. -- Only grant public access to collections that are genuinely intended for unauthenticated consumption. +New projects attach a single policy named **Public** to the Public role, with all permissions off by default. You can attach additional policies in **Settings → User Roles → Public**, and each one carries the same exposure as the default Public policy. When you edit any public policy in **Settings → Access Policies**, the Data Studio shows a warning, and asks you to confirm before saving any change that grants access to a system collection. + +#### Grant Only What Unauthenticated Users Need + +Read access without a filter exposes **all items** in a collection, including items you might assume are hidden by your frontend. + +- Only grant public access to collections intended for unauthenticated consumption. - If only a subset of items should be public (for example, published posts but not drafts), use [custom permissions](/guides/auth/access-control#custom-permissions) to restrict which items and fields are accessible. - Before deploying, test the public endpoints with an unauthenticated request. This is the simplest way to catch unintended exposure. +#### Scope Public File Access to Specific Folders + +Read access on `directus_files` controls both the `/files` endpoint, which lists file records, and the `/assets/:id` endpoint, which serves file contents. Granting a public policy read access on `directus_files` without a filter lets anyone list and download every file in the project. + +This includes files you did not upload yourself. When a user exports items to the File Library, Directus stores the export as a regular file. Large exports are always saved to the File Library. A filter-less public read permission makes these exports, and the collection data inside them, publicly available. + +- Never grant a public policy read access on `directus_files` without a filter. +- Create a dedicated folder for public assets, and add an item rule that limits public read to that folder: + + ```json + { + "folder": { + "_eq": "" + } + } + ``` + +- Use `_in` with a list of folder IDs if you need more than one public folder. +- Store exports and other internal files outside your public folders. Set the **Default Exports Folder** in **Settings → Files & Storage** to a folder no public policy can read, so [exports saved to the File Library](/guides/content/import-export#exports-saved-to-the-file-library) don't land in a public folder or the root. See [Restrict Public File Uploads](#restrict-public-file-uploads) for guidance on public create access. + +#### Avoid Public Permissions on System Collections + +System collections (prefixed with `directus_`) store your project's users, configuration, permissions, and activity. Public access to any of them is rarely intended. The risks described in [Restrict Access to System Collections](#restrict-access-to-system-collections) and [Audit Read Access on Sensitive System Collections](#audit-read-access-on-sensitive-system-collections) apply to every unauthenticated request when the permission is on a public policy. + +- Do not grant a public policy any permission on a `directus_*` collection unless your project requires it. +- If you must grant one, always add an item rule that limits access to the specific records unauthenticated users need, and use field permissions to expose only the required fields. +- Never grant a public policy create, update, or delete access on a system collection. + +::callout{icon="i-lucide-triangle-alert" color="warning"} +Read access without a filter exposes every record in the collection. On a system collection, this can include user details, file metadata, or project configuration. +:: + +#### Audit Existing Public Permissions + +Review every public policy on existing projects. + +1. Open **Settings → User Roles → Public** to see every policy attached to the Public role. Open each one and review every collection listed under **Permissions**. +2. Look for any `directus_*` collection. Remove the permission unless your project requires it. +3. For each remaining read permission, confirm it uses a custom item rule rather than full access. +4. Send unauthenticated requests to the endpoints your project exposes and confirm the responses only contain data you intend to be public. For example, the following request, sent without an access token, should return a permissions error or only files from your public folders: + + ```http + GET /files + ``` + +You can also list every public permission through the API as an administrator. First, list the public policies, which are attached through access rows with no role and no user: + +```http +GET /access?filter[role][_null]=true&filter[user][_null]=true&fields=policy +``` + +Then list the permissions of those policies: + +```http +GET /permissions?filter[policy][_in]=, +``` + +Any result with a `collection` starting with `directus_`, or a read `action` with an empty `permissions` value, needs review. + +If you find that `directus_files` was publicly readable, check the File Library for exports that may have been exposed. Exported files use the title format `export--`. Move them into a folder no public policy can read, or delete them. + ### Scope Junction Table Permissions Junction tables connect two collections in a many-to-many relationship. Unrestricted create access on a junction collection lets a user associate any item on one side with any item on the other, regardless of the permissions on the related collections. A user with create on `posts_tags` can attach any tag to any post, even without read or update access to `posts`. @@ -130,9 +197,9 @@ Since password validation runs on the main event loop and is performed whenever ### Restrict Public File Uploads -The public role has no file upload permission by default, and that is the safe choice. Granting the public role create access on `directus_files` lets any unauthenticated request upload arbitrary files to your storage backend, leading to storage exhaustion, delivery of hostile content from your domain, and abuse of your project as an open file host. +The Public role has no file upload permission by default, and that is the safe choice. Granting a public policy create access on `directus_files` lets any unauthenticated request upload arbitrary files to your storage backend, leading to storage exhaustion, delivery of hostile content from your domain, and abuse of your project as an open file host. -- Do not grant create on `directus_files` to the public role. +- Do not grant create on `directus_files` to a public policy. - Configure [`FILES_MAX_UPLOAD_SIZE`](/configuration/files#upload-limits) and [`FILES_MIME_TYPE_ALLOW_LIST`](/configuration/files#upload-limits) to limit the shape of acceptable uploads. ### Prefer Authorization Headers for Asset Requests