Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

There is a particular string (URL) that I need to send from my webserver to the browser. The browser will need to inspect the string and later send it back unmodified (aka request the resource that the URL is pointing to). If the browser could modify the string, it would be a security risk. So I need to make sure that the string has not been tampered with.

To do that I need to provide some kind of checksum in the URL which the server can validate, but which the client cannot generate. Two approaches come to mind:

  • Append some secret string (aka "salt", preferably pretty lengthy and random) to the URL and hash it with SHA1. Since the client doesn't know the secret string, he shouldn't be able to generate the hash for a different URL. My worry: Since he can gather several of these server-side generated URLs (on the order of a few dozen), maybe he can reverse-engineer the salt?
  • The same as before, but also encrypt it with AES. This is slower, but there's actual cryptography involved. My worry: SHA1 generates 160 bits, while AES works with 128-bit blocks. So the hash will need to be padded, probably with zeroes. Also, there are just 2 blocks. Will that not allow the attacker to discover the key easily?

Is any of these approaches really secure? Or should I try to do something else entirely?

share|improve this question
add comment

6 Answers

up vote 2 down vote accepted

This technique can be implemented reasonably securely. I would view this as an advanced technique for high performance: something you should only do if you're sure you know what you're doing. If you don't need high performance, it's simpler to have the server validate the user is authorised for each request. Having said that, this pattern is actually quite widely used, especially when a dynamic website hands off to a content delivery network.

You should use HMAC to generate your hash. You can use HMAC with any hash; for this purpose SHA-1 is quite adequate. Among other things, this protects you from Length Extension Attacks.

The simplest arrangement is that the hash contains a fixed server secret and the URL. The problem with this is that once a user has a URL with the hash, this is valid forever. You can't revoke a user's access to a particular resource.

We can partially fix this by including a timestamp in the URL. When you generate the hash, use the current timestamp. When you verify a hash, also verify that the timestamp is within a period you consider acceptable (perhaps 24 hours). There are other approaches to fixing this, such as including the user's session ID in the URL, although this would require a database lookup to verify the hash.

share|improve this answer
    
Hugs and kisses! HMAC is just what I needed! Thank you! :) Revoking the access is not necessary in this case. Either the resource is available to anybody, or nobody (in which case I can simply delete it). –  Vilx- Nov 14 '13 at 8:09
add comment

If I had to solve this, I would consider building a temporal session variable or database entry (either of which is retained server side) and would give a generic URL to the client where that generic URL includes the lookup key for the server side URL. The generic URL would determine the proper location and redirect to it.

Since you indicate that the browser having the true URL (or just modifying it to something similar) would be a problem, a better approach might be to simply use one-time URLs that you give out to clients -- you keep the real URL unaccessible through server rules, and you create a link that the client gets; that link is cleaned up when it's no longer necessary.

share|improve this answer
    
These resources are pretty static. I'd like the browser to be able to cache them. –  Vilx- Nov 13 '13 at 17:37
    
If you extend the lifetime of your temporal URLs to something you're comfortable with, you can keep them reasonably static. It won't hurt the browser to reload them once every N days (for whatever N you feel ok with). –  mah Nov 13 '13 at 17:39
    
Not a bad idea, but I'd need somewhere to store the resolutions of the temporal URL's. Hmm... I don't have a DB available (the one I use will not allow something like this to be stored in it). And I'd like my code to be cluster-proof, so I can't store it locally in the filesystem. A session will only last, well, for one session. I'm out of ideas. :P –  Vilx- Nov 13 '13 at 17:44
    
I have a feeling I'll need to return to whitelisting them. :P –  Vilx- Nov 13 '13 at 17:44
add comment

The first rule of crypto is you don't write your own crypto. Hashing is crypto.

You want to ensure the integrity and authenticity of a message. What you need is a Message Authentication Code (MAC). I recommend you use HMAC with an appropriately strong hashing algorithm. There should already be a library or module in your language with a tested implementation of HMAC-SHA1 or HMAC-SHA256, for instance.

share|improve this answer
add comment

Your first method is not a salt. A salt by definition should not have any impact on the security if it is leaked. It is to prevent reversing the hash, not to provide security against faking the hash.

Similarly, a hash itself does not prove anything about the file unless it is signed such that its authenticity can be verified. What you need is a signature for the URL you are passing.

While fundamentally there are some other issues that really make this a non-ideal way of dealing with the situation (and there are likely other problems in the security model as a result), you can sign the hash by either encrypting it with a private key so that it can be verified by both the client and the server or by a symmetric key so that the client is not able to produce a fake hash because it would lack the verification of authenticity since they couldn't produce an encrypted value for the hash.

Don't make your own system, use an existing system for signing a value if you really need to pass the value through the client and have it remain unaltered. Preferably however, you should write your system such that the server can keep track of the values that need to be returned to it and you should link all such values to some session token that you securely exchange with the client (such as using an HTTPS session).

share|improve this answer
add comment

The browser will need to inspect the string and later send it back unmodified (aka request the resource that the URL is pointing to). If the browser could modify the string, it would be a security risk.

I feel that you are approaching the problem from a wrong angle. Why is it a security risk for a browser to modify the URL? That's the job of the browser.

If you want to protect the resource the URL is pointing towards, do it on the server side. Require a form of authentication before the server delivers the requested resource to the browser. This can range from simple session-based authentication to more complex schemes like OAuth. Take your pick.

share|improve this answer
    
The reason I want to protect against tampering is because a hacker might modify the URL and request a resource that he should not be able to get. If my code has generated this URL, that means that it's safe for him to get it (in my case, the permissions on the resources aren't user-specific - either everyone can get it, or nobody can). I could alternatively create and maintain a whitelist of such resources, but it would be a lot more simple (for programming) if I could just add automatic tamper-protection for these URLs. –  Vilx- Nov 13 '13 at 17:07
    
In other words - I've got a bunch of resources on the server. I wish to protect them. Some of them are public (and can be requested from the web), some of them are private (the code uses them, but the public shouldn't see them). Developers add these resources as they see fit. Now I've got a problem - how do I tell if a particular resource (which is being requested) is public or private? One approach is to mark (aka whitelist) them; another (which I'm trying to take) is - if the code generated a link to it, that means it's public. –  Vilx- Nov 13 '13 at 17:39
add comment

You could store the resources being requested in a temporary but random location in the filesystem, and use that URL to retrieve it.

Consider the sites that make you fill out a survey to download a particular PDF. They often return a URL like http://www.example.com/whitepapers/12345abcde/insightful_report.pdf

One way to do this is once the user fills out the survey, generate a random number, create a temp folder by that name, place a copy of the resource in that folder (more specifically, create a symbolic link to the resource in that folder), then return the URL to the client. Every 24 hours sweep the whitepapers folder and delete every directory older than one day.

This solves your problems of persistence and lifetime: use the file system instead of a database. Cleanup is a cron job. As long as you make the random number large enough (I've seen sites using GUIDs for this task), they're unguessable. It doesn't even require any knowledge of crypto, although I'd always recommend using a cryptographically secure pseudo-random number generator.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.