20

I would like to give customers a random-looking order number but use 0, 1, 2, ... in the backend. That way the customer gets a non-password-protected order status URL with the encrypted order number and they cannot look at other customers' order numbers by adding or subtracting 1. This might replace a scheme where random order keys are generated, checked for uniqueness among all the previous orders, and re-generated until unique. When the web server gets a request to view an order, it decrypts the order number and retrieves the order.

To keep the URL short, what "good" encryption algorithm has the shortest block size? Is this scheme a good idea? (What if I was encrypting Apple, Inc. employee ids to keep Steve Jobs from asking for Employee #0?)

Observe that all the package tracking websites allow you to track packages without authentication. It would be fine to limit the amount of information shown on the password-free order status page.

joeforker
  • 40,459
  • 37
  • 151
  • 246
  • Why? Don't you think a [UUID](http://en.wikipedia.org/wiki/UUID) or a [Globally Unique Identifier](http://en.wikipedia.org/wiki/Globally_Unique_Identifier) would be best for that? There are libraries for doing that. – Johannes Weiss Feb 04 '09 at 20:12
  • It's kind of odd that you would put an order status behind a non-password-protected link. Security by obscurity isn't very good security at all. – Kias Aug 24 '14 at 02:24

7 Answers7

13

Most block ciphers are going to use larger than 32-bit sized blocks, for security reasons.

However, I found one that is made specifically for what you are doing: Skip32

You may consider using a GUID, but perhaps you have reasons you want to avoid that. (Say, your app is done already.)

Edit: Actually, if a GUID is permissible, then that gives you a range of 128 bits. You could easily use any other block cipher. The benefit to having a larger space (at the cost of long ID strings) is that you'll have much more protection from people guessing IDs. (Not that it an order ID by itself should be a security token anyways...)

MichaelGG
  • 9,976
  • 1
  • 39
  • 82
  • 1
    Thanks. If the URL length was not a concern, then if I needed to avoid AUTO INCREMENT I'd use GUIDs, and if sequential ids behind the scenes were fine then I would use AES. Unlike GUIDs, AES lets me avoid storing lengthy identifiers in a unique index for every order. – joeforker Feb 04 '09 at 21:03
  • 1
    Good answer. For completeness I'll mention that using cleartext order number and appending N bits of a keyed HMAC would be another alternative, and maybe simpler depending on what you have at hand. – Liudvikas Bukys Feb 05 '09 at 02:28
  • 3
    @Liudvikas: concealing the order number is one of the main points of this exercise, that way the customer cannot guess how small a company we are by how slowly our order numbers increment. – joeforker Feb 05 '09 at 15:14
4

Recently I started using Hashids set of small libraries. The idea is to encrypt a number or list of numbers into hashed string like:

12345 => "NkK9"
[683, 94108, 123, 5] => "aBMswoO2UB3Sj"

The libraries are implemented in popular programming languages by various authors. They are also cross-compatible, which means you can encode the number in Python and then decode it JavaScript. It supports salts, alphabet definition and even exclusion of bad words.

Python:

hashids = Hashids(salt="this is my salt")
id = hashids.encode(683, 94108, 123, 5)

JS:

var hashids = new Hashids("this is my salt"),
numbers = hashids.decode("aBMswoO2UB3Sj");

This is not govt proof encryption but totally sufficient for some non-predictable permalink sharing sites.

gertas
  • 16,869
  • 1
  • 76
  • 58
4

If your idea is that just knowing the order number (or URL) is enough to get information on the order then:

  • The order number space needs to be extremely large, otherwise attackers and/or customers will conceivably search the order space, to see what can be seen.
  • You should consider that an attacker may launch gradual probing from numerous machines, and may be patient.
  • Probing the order number space can be mitigated by rate limiting, but that's very hard to apply in a web environment -- it's hard to distinguish your customer access from attacker access.
  • Consider also that the order number is not much of a secret, people could be sending around in emails; once it's out, it's impossible to retract.

So, for the convenience of one-click check-my-order-without-logging-in, you have created a permanent security risk.

Even if you make the order number space huge, you still have the problem that those URLs are floating around out there, maybe in possession of folks who shouldn't have gotten them.

It would be much much better to require a login session in order to see anything, then only show them the orders they're authorized to see. Then you don't have to worry about hiding order numbers or attackers guessing order numbers, because just the order number isn't enough information to access anything.

Liudvikas Bukys
  • 5,790
  • 3
  • 25
  • 36
  • 1
    Good points, but it doesn't have to be a big deal if an attacker knows whether someone's order shipped or not (they could attack UPS instead). The password-free order link could be designed to not display valuable secrets. – joeforker Feb 04 '09 at 21:09
2

Issues of whether you should actually be doing this aside, here's a very simple block cipher with a fixed key (since you only seem to need one permutation anyway).

static uint permute(uint id)
{
  uint R = id & 0xFFFF, L = (id>>16) ^ (((((R>>5)^(R<<2)) + ((R>>3)^(R<<4))) ^ ((R^0x79b9) + R)) & 0xFFFF);
  R ^= ((((L>>5)^(L<<2)) + ((L>>3)^(L<<4))) ^ ((L^0xf372) + L)) & 0xFFFF;
  return ((L ^ ((((R>>5)^(R<<2)) + ((R>>3)^(R<<4))) ^ ((R^0x6d2b) + R))) << 16) | R;
}

Skip32 is much better as far as 32-bit block ciphers go, but it's a bit heavyweight when three (long) lines would do. :-)

Adam M.
  • 189
  • 5
  • 2
    Does it have a name or heritage? – joeforker Mar 15 '12 at 13:42
  • 1
    This? Not really. I made it up after reading about Feistel networks. I called it "syfer" in my code, and that version accepted a 32-bit key as well. (That version is [here](http://stackoverflow.com/questions/1971657/is-there-any-block-cipher-that-have-a-block-size-of-32-bit-for-use-on-net/9718378#9718378).) Some of the constants may have been borrowed or adapted from XXTEA, although I don't quite remember. I use it to generate permutations of the integers. I don't recommend it for security. – Adam M. Oct 28 '13 at 22:52
1

I prototyped this idea using Blowfish, a block cipher with 64-bit blocks.

joeforker
  • 40,459
  • 37
  • 151
  • 246
0

I don't think this scheme is that great of an idea. Why aren't you verifying that a user is logged in and has access to view a specified order?

If you REALLY want to just have all orders out there without any authentication, a GUID would be best.

Or, you could try to come up with order numbers prefixed with something about the customer. Like (PhoneNumber)(1...100)

Bob
  • 97,670
  • 29
  • 122
  • 130
0

To meet the requirement you could simply use a hash such as SHA-1 or MD5 on your indexes. These will provide the adequate security you require.

To bring down the size you can change to a different encoding; such as 64 bit.

I'd also very strongly recommend insist on using a salt, otherwise the hash values could easily be broken.

Gavin Miller
  • 43,168
  • 21
  • 122
  • 188
  • @Michael - You're missing one of the requirements of the question and that is using linear indexing. – Gavin Miller Feb 04 '09 at 20:18
  • With a name like that, I'm disappointed you didn't suggest something based on a linear feedback shift register. – joeforker Feb 04 '09 at 20:19
  • @LFSR - I didn't see any linear indexing requirement. He said he wanted natural numbers on the backend, and "random" on the front end. A cipher block fits perfectly. A hash doesn't, since it's not unique. – MichaelGG Feb 04 '09 at 20:25
  • @Michael - Sorry, I'm missing something. It's a unique order number with a hash applied? How is it that that would not be unique? – Gavin Miller Feb 04 '09 at 20:30
  • Hashes are not guaranteed to be unique over any input, that's why. So, in this case, he's performing H(SALT + ID). He'd have to precompute and check every ID to ensure it's unique. (Sure, SHA1 over a few thousand order ID is _probably_ going to be ok, but he'd have to prove it by hand.) – MichaelGG Feb 04 '09 at 20:51
  • 2
    Oh, I should also note, if you only expose the hash in URLs, that means you need to compute and store the hash, since you can't reverse it except through brute force. – MichaelGG Feb 04 '09 at 20:57
  • Ballsy programmers would trust the hash code, it's an incredibly tiny risk with SHA1 and its peers. For example, git stores every revision of every file by hash and it works fine. For this question if I wanted at least 128 bits I'd use AES. The tricky part is having a short url. – joeforker Feb 04 '09 at 20:57