How to protect /uploads route with a custom policy in Strapi V4
Strapi is an awesome headless CMS, and with its version 4, things got even better. However, what was already an issue on v3 is the unprotected serving of anything under your /public folder. You might think this ain’t an issue, as the folder says its content should be public, right? Wrong, because everything you upload through the Strapi interface in the Media Library or basically everywhere in the Content-Manager, goes straight to /public/uploads
So, why is this an issue?
Imagine you are having Strapi as a Backend for an invoicing system, or anything with private and secret files. If you upload them through the Strapi backend, they will be available right away through yourwebsite.com/uploads/filename.ext
If you use the upload plugin for user created content too, those will be public as well. There is also no way to prevent this from the interface!
In V3, it was enough to overwrite the /uploads path from anywhere, but with V4 this is no longer possible as everything is prefixed with /api, so no way to overwrite the behavior. After much research, and reading through the actual code of Strapi (the benefits of open source), I found a working solution, by overwriting the default route of Strapi:
Let’s fix it
You can do this anywhere in your code, but I suggest to do it as an extension of the upload plugin, to keep everything organized. So lets get started:
- In your project, navigate to /src/extensions/upload (or create the path if it does not exist)
- Create a file named strapi-server.js
- Add the following code:
So, what are we doing here?
In line 4–7 we are creating some default values, these are equal to the Strapi defaults. Then, we start exporting the plugin:
Line 9: Grab the defaultIndex and maxAge, if it was set somewhere else.
Line 11: Call the strapi.server.routes function, to overwrite any of the default routes. (Check out node_modules/@strapi/strapi/lib/middlewares/public/index.js if you want to see what else can be overwritten)
Line 13–18: Defines which route we are overwriting, and who is handling the calls, in this case it is the Koa Static serving that handles everything.
Line 21–30: Here is the interesting part. You can add any type of policy here (see here for more info: Strapi Docs ). In this case, we are just looking for the first parameter (which is the called route, so e.g. /uploads/test.txt), and checking if it contains a specific extension, e.g. .txt. Again, nothing fancy but you can add any kind of policy here that you need! Return true if access should be granted, and false if not.
And that’s it! You secured your /uploads route with your own policy. It is still a mystery to me, why this is not available by default in Strapi, but at least you can work around it this way!
