3

I'm having trouble designing a system for authenticated content from my site distributed by other people. I'm working in Ruby on Rails, but I don't think that at a theoretical level my question isn't restricted to that framework. In order to not give too much away, my site (let's call it www.site.com) helps businesses keep inventory of who has purchased what item, and allows customers to view their purchase history.

Hence, I have the following problem:

  1. Suppose Alice sells cheeseburgers. Alice makes an account on site.com and registers her cheeseburgers as an item that she sells. Consequently, site.com stores Alice's cheeseburger in a SQL database with the id 92314
  2. Bob is a customer at Alice's cheeseburger stand and has recently purchased a cheeseburger.
  3. Alice would like to tell Bob about site.com and its ability to track purchases. Hence, Alice sends a request to site.com to return hashA that corresponds to item with id 92314.
  4. Bob visits www.site.com/purchase/hashA and is prompted to either create an account or sign in to log his new cheeseburger purchase from Alice's Delicious and Delectable cheeseburgers.
  5. Charlie also visits Alice's cheeseburger stand and purchases a cheeseburger.
  6. Alice would also like to tell Charlie about site.com and sends a request that generates hashB for Charlie to use in the same manner.

My question is, how do I come up with a scheme that can generate hashA and hashB in a way that my web server can efficiently lookup the items. I suspect that hashB can be generated from hashA using some sort cipher using hashA as a seed. I don't know how, upon receiving hashA or hashB, site.com will know that it corresponds to item 92314. I suspect that using a nonce will help.

Things that I would like to avoid include:

  • Having Charlie use hashA after it has been used by Bob
  • Exposing the id 92314 to the user
  • Having a single point of failure (i.e. a single key that encrypts hashes for all ids)
  • Having things break if Charlie uses hashB before Bob uses hashA.

Disclaimer: My expertise is more in computer graphics and less in web development or cryptography, so pointing me towards things to Google is helpful. If this belongs in any of the other stackexchange sites (Cryptography, Programmers, etc). Feel free to let me know and I will move it.

2 Answers 2

4

If I understand your problem correctly:

  • Alice is authenticated with the Server. When Alice talks to the Server, the Server knows that it is Alice.
  • Alice has an "item" on the Server corresponding to some ID in the database.
  • Alice may grant some sort of access to the item, to persons of her choice, e.g. Bob and Charlie.
  • Bob and Charlie will connect to the Server afterwards, using a token that was sent to them by Alice, and which represents the access right on the server.
  • The ID must not be revealed to anyone outside of the server. Tokens for Bob and Charlie must be distinct from each other, and not be guessable.

So this points to this generic solution:

  • When Alice obtains a new token T from the Server, the Server remembers that "the token value T is for item 92314". This memory is, say, a new row in a dedicated database table.
  • Each time Alice requests a new token T, the Server generates one randomly. If the token values are large enough (16 bytes or more), then probability that the Server generates twice the same token value is negligible. Alternatively, since the Server remembers all produced tokens, it can enforce unicity.
  • Optionally, the token T may be time-limited (the Server forgets emitted tokens after some time) and/or one-shot (the Server forgets emitted tokens after usage).
  • Optionally, when Alice obtains a token T, she may tell to the server "this is for Bob" so that when Bob connects, parts of the registration form are already filled.

With a time limit, the number of tokens to remember, with their association to the item ID and optional user information, can remain bounded, and thus be technologically manageable.

An optimization is to offload this memory onto Bob. This means that the server will encode into the token value itself whatever it would have liked to remember. In that sense, memory is free. However, this requires cryptography, because:

  • It should not be feasible for outsiders to generate token values that the Server will accept; token generation should be a Server monopoly. This points to the need for a MAC.

  • The token contents, that the Server wants to remember, are confidential. So encryption is needed as well.

In both cases, there are keys, and that's unavoidable: keys are knowledge, and in cryptography, knowledge is power. Everybody can buy the same computers, so if the Server can generate token values and other people cannot, then the Server must know something that the other people do not. That "something" is what cryptographers call a key. Cryptography can help you concentrate that "knowledge" into a very small, non mutating value, say a sequence of 128 bits (16 bytes). But it cannot make it disappear altogether.

The scheme for this "memory-less" Server is then:

  • Given data D, token value T is the encryption+MAC of D. In order to make tokens for Bob and Charlie distinct, D may contain a global counter, the date of production, and/or some random bytes. D will also contain the item ID, and everything that the Server wants to "remember" about this token.

  • When receiving a token T, the Server checks the MAC and decrypts it, obtaining D, thus everything that is need for registering Bob (or Charlie).

  • Optionally, when Bob's account is created, the token value T used by Bob is recorded in Bob's account. Thus, when a token T' is received, the Server can check whether T' has not already been used to create an account. This is to prevent Bob himself from giving copies of his token to his friends. This implements a "one-shot" property for the token.

Combining encryption and MAC properly is a matter of subtlety. If you do this road, you are encouraged to use an encryption mode which does both the encryption and the MAC in the cryptographically correct way; i.e. GCM or EAX.

3
  • Great answer! I think I'm going to use the non-optimized way for now until performance becomes an issue. For the sake of discussion, how smart or not smart would it be to generate keys every hour, let's say, and have Alice create a token that encodes a date, given data, and a MAC of the combination. Then using the date the server can lookup the key used to do the encryption of the data.
    – Mokosha
    Commented Sep 30, 2013 at 14:01
  • Managing multiple keys implies extra complexity, and complexity is bad for security, so regenerating the key regularly should be envisioned only if it provides a clear improvement -- which does not seem much the case here. What would give some extra protection would be to keep the key in RAM only, but this means that all published tokens are invalidated when the server reboots, which may be inappropriate. Commented Sep 30, 2013 at 14:49
  • The important point here is that a sufficiently large, random key (128 bits) will not be brute-forced, not in one hour, not in ten years either. If the attacker obtains it, it will be through stealing a backup tape or an old disk, or some SQL injection attack. So there is no actual gain in making the key short-lived; but there is in never writing the key in a file. Which may or may not be workable in your usage context. Commented Sep 30, 2013 at 14:51
5

Whoa! You're overcomplicating the issue to a high degree; the matter at hand is way simpler.

Table 1 (products)

product_id - product_name - product_key

product_key can simply be the sha1() of product_id, product_name, and some random bytes. Or, better, just grab random 16 bytes and pass them to sha1().

Table 2 (requests/invitations)

request_id - product_id - request_key - valid

request_key is generated the same way: the sha1() product of request_id, product_id, and some random bytes. Or, simply, the sha1() of some 16 random bytes.

Now, when Alice wants to tell Bob about site.com, a row in "Table 2" will be generated, and she'll simply send him the link site.com/purchase/request_key.

Same thing happens when telling Charlie; a new row in "Table 2" will be generated (new request_key), thus, a new link. Once the link is visited, you check if valid is true. When the key is used, you set valid to false.

  • The key can only be used once.
  • The ID isn't exposed.
  • No fancy encryption.
  • Each request key is independent.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .