Here's how I do that.
1. Put the files in a directory outside of the web root. That way nobody can access them from the web. Let's say /path/to/files/.
2. Create a Media Source with its path set to /path/to/files/, and the relative option set to no. I ignore the URL settings, since they have no relevance in the case of files outside of the web root (no URL can reach them).
3. Create a chunk, pathPrefix, with this same path in it. The reason why will become clear later.
4. Create a custom Content Type for your files, for example for .pdf files. Make sure they're set as binary.
5. Now, for each file, create a Static Resource. You will be able to select your Media Source, and upload and select files within the path you set for your Media Source. (rest/of/path/to/file.pdf). The Static Resource can have templates, TVs, summary, every field normal resources have except the content will be that inner path. For the alias, skip the .pdf part, MODx will add that automatically.
Here's where the chunk comes in. Since Static Resources don't really work with Media Sources, what you see in the resource's content field will be the path MODx tries to work with - rest/of/path/to/file.pdf. So, put the chunk tags in front of the path:
[[$pathPrefix]]rest/of/path/to/file.pdf
Now when MODx processes the resource, the chunk's content will be added in front of the resource's content, resulting in the full /path/to/files/rest/of/path/to/file.pdf.
6. In the resource's Settings tab, set the Content Type to your custom type (PDF, for example), and set the Content Disposition to Attachment. This will cause the browser to deal with the file as it is configured to do so, either loading a PDF reader browser plugin or opening a download dialog. This will also tell MODx what suffix to use for the alias - file.pdf
When MODx processes a binary Static Resource, it sets the appropriate HTTP headers and streams the file.
Now you can treat this Static Resource as you would any resource. Assign it to resource groups, use search snippets on its fields (except its content field) and tagging snippets on its TVs, even use getResources to have nice lists with images and summaries if you like.
The user will only see the URL to the static resource, never the path to the file.