5

Scenario:

  • User requests page on my site
  • Server builds up token using various values, encrypts it (via an industry-standard algorithm, nothing homegrown) using a key only the server knows, and returns it as part of the page data
  • Token is embedded into link on page
  • When client clicks that link, token is sent to server, decrypted and parsed and the values used to determine what to do

This is done because we need certain bits of information to be sent from the client to the server, but we don't want to leak those bits of information to the client because they would reveal details of the server's inner workings.

The issue here is that, every time a particular user views a particular page, the same token will be generated for them. Apart from allowing the same request to be replayed against the endpoint ad infinitum, this makes it more likely that someone will be able to break the token encryption (yes, I know this isn't likely, but it is possible).

To make the encryption even more resilient, my intention is to insert a high-quality random value - say a GUID - into the token before it is encrypted. The end result will be to introduce more entropy into the encryption process, meaning that for a given set of identical values that we care about (i.e. everything except the random value), the generated token will almost certainly differ. This random value will always be inserted in the same place and will always be the same length, so removing and throwing it away after the token is decrypted is trivial. Effectively, this is augmenting encryption with obfuscation.

It appears to my untrained eye that this is a relatively simple and safe solution to my problem, but is it, or am I just wasting my time and/or making things less secure and more complicated?

  • i would consider a standard approach like JWT, which includes space for your side-car meta – dandavis Aug 30 '18 at 18:47
  • Which encryption algorithm do you use? – Sjoerd Aug 30 '18 at 19:14
  • 1
    Why should the secret leave the server in the first place? Generate a random token. That one is given to the client. The secret data is stored on the server, with the token as lookup key. Client sends token to server. Server looks up values in database. A secret can't be compromised if you don't give it away.If the token is used on another server, make that server talk to the original server over an encrypted channel. If the client isn't supposed to have the data, don't give it to him. – Polygnome Aug 30 '18 at 20:51
15

You don't need to add any randomness to an encrypted token. If it's properly encrypted, user has no way to infer anything about the encrypted data.

The issue here is that, every time a particular user views a particular page, the same token will be generated for them.

This seems odd. Your encryption does not seem to use a secure standard. Otherwise every time you encrypted the same token, the cyphertext would change. You must use an IV, and never ever think about AES-ECB, that mode is broken and should not be used even to teach kids how AES works.

If you use industry standards, you will know about the Initialization Vector (IV). In rough terms, the IV is a value added to the process that changes the cyphertext, so even if you encrypt two identical plaintexts with the same key, the corresponding cyphertext will change.

The IV is the random data you are thinking about adding. It's already there.

  • Since OP says that he sees the same token generated, I guess it's not properly encrypted with an IV. In that case, maybe he should add it or change what he's using for encryption to something that does add it for him. – JoL Aug 30 '18 at 17:42
  • When encrypting a large amount of data, the cost of storing or transmitting an IV with the encrypted data will be usually be small relative to the cost of storing or transmitting everything else. If the payload is small, however, the cost of the IV relative to the payload may be more significant. – supercat Aug 30 '18 at 21:08
  • If you don't use an IV and the data is small and predictable, you will disclose your key. And as you have one single key on the server, losing that key means disaster... – ThoriumBR Aug 30 '18 at 23:40
4

Let's try to break this down:

Problem: replaying a token:

Apart from allowing the same request to be replayed against the endpoint ad infinitum

Usually auth tokens solve this problem by expiring the tokens: the encrypted data includes a datestamp (which has the side-effect of making each token unique) and the server will reject any token older than, say 15 minutes.

Problem: breaking crypto:

this makes it more likely that someone will be able to break the token encryption

I'm not an expert, but it would seem to me that you should use a random IV for each encryption, and include the IV with the cipher text as part of the token. This would both solve the cryptanalysis problem you're alluding to, and have the side-effect of making your tokens unique. (if you're not using random IVs, then you probably have much bigger problems on your hands)

Why not use a standard library to do this rather than going out of your depth to design your own encryption scheme? I would recommend JWT.


Finally, as a public service I need to correct this: GUIDs ARE NOT HIGH-QUALITY RANDOMNESS.

See Wikipedia/Universally_unique_identifier.

UUIDs / GUIDs are designed to be unique, they make no attempt to be unpredictable. There are 5 standard ways to derive a UUID; 4 of them are some combination of MAC address + hostname + timestamp. Version 4 UUIDs are pure random, but because UUIDs / GUIDs generation needs to be fast and non-blocking, they use non-cryptographic random number generators.

PLEASE DO NOT USE UUIDs / GUIDs AS A SOURCE OF CRYPTOGRAPHIC RANDOMNESS. Instead use /dev/random in unix, SecureRandomin java, or whatever the equivalent is in your OS / programming language

  • 2
    don't you mean /dev/urandom? devs shouldn't use /random for anything unless you are an expert making your own crypto system, and even then; don't roll your own crypto. – dandavis Aug 30 '18 at 18:45
  • 3
    @dandavis uuuggggg I'm not getting into this debate again. They both have pros and cons. random is safer but may have performance issues, urandom is secure if your your system has been up for a while, is not an IoT device. etc. On most non-linux unixes /dev/random and /dev/urandom are symlinks to each other (ex. BSD, MacOS and iOS, Solaris (I think), AIX, etc). Personally I like FreeBSD's implementation better than linux's, but *shrug*. Basically: yes I know the difference, no I don't think they're significant enough to have yet another debate over. – Mike Ounsworth Aug 30 '18 at 18:56
  • 1
    heh, no debate needed; i think your comment provides the context the answer missed... – dandavis Aug 30 '18 at 19:02
1

The solution that is used on my platform of choice is "encrypt/decrypt with managed IV (Initialization Vector)." What this means is that, upon encryption, the entire plain text is encrypted with a cryptographic secure IV, and then the IV is placed in base64-format before the beginning of the cipher text. Upon decryption, the IV is pulled off the front and used as the IV for the cipher text. This is all transparent to the developer. Note that it is perfectly safe to expose the IV, because decryption with the correct key but wrong IV will result in a failure condition. If your programming language has a similar feature, do use it; by having a random IV generated each time you encrypt something, you don't need to put any extra data in the plain text.

0

To me, this looks like an XY Problem.

The best way to protect a secret is not to tell anyone the secret. The token is generated server-side. The server already knows the data. And then you replay the token to the server, which contains only data the server already has. That is completely unnecessary.

Other answers have pointed out the quick, immediate fix for your encryption problem by using encryption with IVs. But the better solution would be to make this superfluous.

Server builds up token using various values, encrypts it (via an industry-standard algorithm, nothing homegrown) using a key only the server knows, and returns it as part of the page data

Instead of encrypting those values, save them in a database. You can potentially, if you really must, still encrypt those values and then save them. But there is absolutely no need for these values to ever leave the server, even in encrypted form. Then generate a truly random token (with a CSPRNG), and use that as a lookup-key. Send this token to the client.

Token is embedded into link on page

Use the random token you just generated for that.

When client clicks that link, token is sent to server, decrypted and parsed and the values used to determine what to do

Instead of decrypting and parsing the values, you use the token to look them up in the database. Done. If you only stored the data encrypted, you still need to decrypt it, of course.

This also works if you have multiple servers. If the token is generated on Server A and send to Server B, have Server B use a secure connection to Server A and request the data via the token from Server A. In this scenario, too, the data is never sent to the client.

This is done because we need certain bits of information to be sent from the client to the server, but we don't want to leak those bits of information to the client because they would reveal details of the server's inner workings

Internal state should never leak from an implementation. This is already a code smell and a good hint that your architecture is flawed.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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