Security design¶
← README · Architecture · Roadmap · Releases
What Paperless Unified Search protects, what it trusts and which risks remain. SECURITY.md says how to report a vulnerability and how to verify a release, and argues why the repository and its releases are safe.
What you can expect¶
- Only administrators of Nextcloud can see and change the settings. Every request passes Nextcloud's login and its CSRF check.
- The Paperless API token is stored in Nextcloud's credentials manager and never leaves the server: no response, page or message of the app contains it.
- A Paperless document appears in the results of a user only when that user can open a file of the archive account whose name carries its marker
[P<ID>]: their own if they are that account, or one it shared with them. A file that only carries the marker in its name stands for no document. Selecting the result opens that file in Nextcloud. - Search terms reach Paperless only when the user switches on Search connected services, or when an administrator has marked Paperless as trusted. They travel from server to server, never from the browser to Paperless.
- Requests to Paperless go through Nextcloud's HTTP client, which checks TLS certificates.
- A failed search is logged with the kind of error only, without the term, the URL or the token. The history of the administration settings shows it with its message as well, as it shows a request that only its second try answered; the message loses the token, the term and the query of every URL first.
What is protected¶
| Asset | Where it lives | Protection |
|---|---|---|
| The Paperless API token | Nextcloud's credentials manager | Stored only there; the settings carry only whether a token is configured; saving without a new token keeps the stored one |
| The search terms of the users | Sent to Paperless | Only when the user asks for it or an administrator trusts Paperless; never logged by the app; removed from the messages of the history of the diagnostics, as is the query of every URL |
| Documents in Paperless | Paperless | Read through a dedicated account with read access only; shown only through a file of the archive account that the user can read |
| The configuration | Nextcloud's app configuration | Changed only by administrators, after a test of the connection |
Trust boundaries¶
flowchart LR
browser["Browser of an administrator"]
paperless[("Paperless-ngx")]
subgraph server["Nextcloud server"]
search["Unified search<br/>of a user"]
app["Paperless Unified Search"]
files[("Files of Nextcloud")]
credentials[("Credentials manager<br/>the token")]
end
browser -- "1 · settings" --> app
search -- "2 · term and page" --> app
app -- "3 · term and token,<br/>an untrusted answer" --> paperless
app -- "4 · folders of the user,<br/>files of the archive account" --> files
app --- credentials
- Browser → app. The routes of the settings page pass Nextcloud's login, its CSRF check and the check of the administrator. The URL must use
httporhttpsand carry no credentials, query or fragment. - Nextcloud's search → app. The term and the cursor of a page come from the user: the cursor must be a number, and a page holds at most 50 results.
- App → Paperless. Every answer is untrusted input. It must be JSON with a list of results; a document without a numeric ID is left out; titles and dates are used only when they are text; the excerpt loses every HTML tag and is cut to 180 characters.
- App → files of Nextcloud. The file locator searches only the folders of the searching user, with Nextcloud's permissions, and takes only files, never folders, and only those that the archive account owns. The marker in a name is chosen by whoever names the file and proves nothing by itself; the files of the archive account reach other users only through its shares.
Threats and countermeasures¶
| Threat | Countermeasure | Evidence |
|---|---|---|
| Another site uses the session of an administrator | Nextcloud checks the CSRF token of every request; no route of the app opts out of it or of the check of the administrator | the controller in lib/Controller/, which carries no NoCSRFRequired, NoAdminRequired or PublicPage attribute |
| The API token reaches the browser | The token stays in the credentials manager; the settings carry only whether one is configured | testPublicConfigNeverContainsToken in tests/Unit/Service/ConfigServiceTest.php |
| A malformed or hostile answer of Paperless | The answer is checked for its shape; documents without a usable ID are left out; a failing search returns no results instead of an error | testAWrongAnswerOfPaperlessFails, testDocumentsWithoutAUsableIdAreLeftOut, testAFailedSearchIsLoggedWithoutItsDetails |
| Text of a document runs as script in the search | The excerpt loses every HTML tag; Nextcloud's search shows titles and excerpts as text | testTitleAndSublineComeFromTheDocument in tests/Unit/Search/PaperlessSearchProviderTest.php |
| A user names a file with the marker of a document that they may not see | Only files that the archive account owns stand for documents, and users receive them only through its shares; without an archive account the search shows no documents and doesn't ask Paperless | testIgnoresFilesThatTheArchiveAccountDoesNotOwn, testAFileOfAnotherAccountShowsNoDocument, testWithoutAnArchiveAccountPaperlessIsNotAsked, and the forged file of the end-to-end tests |
| A slow Paperless blocks the search | A connection timeout of 3 seconds and a timeout of 10 seconds for every request, and a second try only for a request that got no answer at all | lib/Service/PaperlessApiService.php, testARequestWithoutAnswerGetsASecondTry, testAnAnswerOfPaperlessGetsNoSecondTry |
| An administrator learns the search terms of the users or the token from the history of the diagnostics | Every stored message loses the token, the term and the query and fragment of every URL, and only administrators can open the settings or clear the history | testAFailureIsRecordedWithoutTheTokenTheTermAndTheQuery in tests/Unit/Service/SearchDiagnosticsTest.php, testAFailedSearchLeavesOutTheTokenAndTheTerm, testARequestThatOnlyItsSecondTryAnswersGoesIntoTheHistory |
The Docker end-to-end tests run the real search endpoint of every supported Nextcloud version against a mock of the Paperless API, with users who may and may not read a file, a file that only carries the marker and a share of the archive account (tests/e2e/README.md).
Residual risks¶
- The marker
[P<ID>]ties a file of the archive account to a document. Whoever may write into a folder of the archive account, for example through a share with write permission, can give a file of that account the marker of any document and see its title and excerpt. Share the archive read-only. - The permissions of the Paperless account decide which documents the search can find. Use a dedicated account with only the read permissions that the search needs, as the README describes.
- With Always include Paperless in global search on, every search term of every user reaches Paperless. Turn it on only for a Paperless server that you trust as much as Nextcloud.
- An
httpURL is allowed, for a Paperless in a trusted local network; then the token and the search terms travel unencrypted. Usehttpswhenever the connection leaves such a network.