TL;DR
Don't do it. Just use authentication and 404s.
How
Fake encrypted data is just random data, from a CSPRNG or TRNG. You don't need to actually run any encryption. To keep attackers from requesting twice and dismissing any file that changes as fake, seed the algorithm with something based on the URL. For example, the SHA-256 hash of the URL. You don't need a cryptographically secure password hash; you're not worried about people finding collisions. In pseudocode:
def handle_request(filename, user):
if file_exists(filename):
return Response(200, get_file(filename))
else:
rand = Random(sha256(filename))
file = range(10000).map(rand.next).to_string()
return Response(200, file)
Why not
But there is a problem. You're burning CPU generating random data for every single response. I could take down your site with this script running on enough machines:
num=0
while true; do
echo curl "https://your-site.com/invalid_url${num}"
((num++))
done | parallel
Oh, look. Now your server is tied up spewing random data out to me, as fast as my CPU and network can handle it. And that's quite possibly the most basic incarnation of a DoS script. More sophisticated attempts will use multiple machines, dynamically increase the number of concurrent requests until it hits a bottleneck, etc.
Of course, it's also possible to trigger something similar if someone bookmarks a page and their browser pings it occasionally to update the favicon. Or for someone to be totally sure they got the right document, so they keep hitting refresh, trying to figure out why it doesn't work.
In addition, if there's a database leak -- something you seem to be worried about, judging by your comments -- then your 'solution' instantly dies. Any attacker with access to the database will be able to see all the existing filepaths and get access to them just by going to the URL.
Instead
The usual solution is to have some sort of authentication scheme, then send a 404 no matter why it can't be accessed (doesn't exist, no permission, etc.), not to generate fake files. You could still be DoSed by a dedicated enough attacker, of course, but spending less to answer every request means that they have to send many more requests, and are easier to detect and stop as a result. Plus, it's easier to explain "If we can't find a file tied to your account, we assume it doesn't exist and tell you that" than "We generated fake information and showed that to you instead of the information you requested" to an irate customer.
In pseudocode:
def handle_request(filename, user):
if user.logged_in() and file_exists(filename) and user.has_access(filename):
return Response(200, get_file(filename))
else:
return Response(404)
You also don't need to use user-based authentication, if you're worried about, say, attackers learning that user nic-hartley
and user inferior-nicks
are actually working together, because you can see that they can both access files 1542, 1092, and 5840. You could require a password, and check file_exists(filename) and password_correct(pass, filename)
. You could even do something more complex; there are a lot of authentication options. However, note that metadata like that could only leak if there's a database leak, and this solution is still better, because you've only leaked the associations and not access to all of the encrypted files.