5

I'm using mysql database auto-increment as an order ID. When I display the order ID to the user, I want to somehow mask/obfuscate it.

Why?

  • So at first glance, it is obvious to admin users what the number refers to (orders start with 10, customers start with 20 etc)
  • To hide, at first glance, that this is only my 4th order.

Based on this this answer, I want the masked/obfuscated order id to:

  • Be only numbers
  • Consistent length (if possible)
  • Not cause collisions
  • Be reversible so I can decode it and get the original ID

How would I acheive this in PHP? It doesn't have to be very complex, just so at first glance it's not obvious.

Community
  • 1
  • 1
paul
  • 731
  • 2
  • 9
  • 13
  • Add the current timestamp to the beginning/end? `$uid = time() . $idFromDb;`. So long as the ID from the database is different you won't get collisions and it will only be numbers. You will get slightly different lengths though as your auto-incremented number increases. – naththedeveloper Nov 19 '13 at 12:29
  • I've updated my question, but I need to be able to convert it back to the original database ID. – paul Nov 19 '13 at 12:32
  • Add some kind of delimiter character to it? `$uid = $idFromDb . '@' . time();` then work from that delimiter in your code to get `$idFromDb` again? There are loads of ways to do this, it's going to be opinion-based as to what method/tactic you take. – naththedeveloper Nov 19 '13 at 12:35
  • Not too sure how this would work, I basically ignore everything after the @ symbol to get the ID? Might be a bit obvious – paul Nov 19 '13 at 12:37
  • You can most probably find your question already answered here: http://stackoverflow.com/questions/9465369/a-good-practice-for-creating-human-typable-non-sequential-unique-ids – Kaii Nov 19 '13 at 12:46

5 Answers5

12

I think you can use XOR operator to hide "at first glance" for example (MySQL example):

(id*121) ^ 2342323

Where 2342323 and 121 are "magic" numbers - templates for the order number. To reverse:

(OrderNum ^ 2342323)/121

Additional advantage in this case - you can validate OrderNumber (to avoid spam or something like this in online form) if (OrderNum ^ 2342323) is divided by 121 with no remainder.

SQLFiddle demo

valex
  • 23,966
  • 7
  • 43
  • 60
2

A little bit late, but Optimus (https://github.com/jenssegers/optimus) does exactly what is here asked for.

$encoded = $optimus->encode(20); // 1535832388
$original = $optimus->decode(1535832388); // 20

Only the initial setup is a bit weird (generate primenumbers)

marcus
  • 699
  • 11
  • 33
1

Can it help?

echo hexdec(uniqid());

Off course you should store this value at db, at the same row with order id.

Viacheslav Kondratiuk
  • 8,493
  • 9
  • 49
  • 81
1

Probably the simplest way is to just generate a long random string and use it instead of the auto-increment ID. Or maybe use it alongside the auto-increment ID. If the string is long enough and random enough, it will be unique for every record (think of GUIDs). Then you can display these to the user and not worry about anything.

Vilx-
  • 104,512
  • 87
  • 279
  • 422
  • I already have a unique database ID, I want to base it on that value so I don't have to check it is unique. – paul Nov 19 '13 at 12:33
  • 1
    @paul - As I said, if you make it long enough and random enough (like GUID/UUID), then you don't **need** to check it for uniqueness. Statistically, the sun will burn out before you'll generate two equal guids. Even if you use the fastest hardware available to continuously generate GUIDs at maximum speed. – Vilx- Nov 19 '13 at 15:13
  • How would I create something like that in PHP? That is only numbers and if possible the same length? – paul Nov 19 '13 at 15:21
  • 1
    @paul - First you use a [cryptographically strong random function](http://php.net/manual/en/function.openssl-random-pseudo-bytes.php) to get a bunch of random bytes (say, 30). Then with the [`ord`](http://us2.php.net/manual/en/function.ord.php) function you convert each byte to a number and concatenate all that together, getting a long, random string of digits. It'll be between 30 and 90 digits in length. Take the first 30 digits and you're done! – Vilx- Nov 19 '13 at 15:42
  • To make it more readable for end users I want the total number to be 20 digits. I'm taking the first 18 digits and adding 2 on to indicate an order/payment etc. This will increase the chances of a collision, but will it increase the chances of a collision so much that I need to be worried? – paul Nov 20 '13 at 12:20
  • 1
    Well, 18 digits is 10^18. A GUID is 5.3*10^36. So it's a lot lower. Still, that's a pretty wildly large number. I'd say your chances are pretty low, as long as you use a proper cryptographically-strong random number generator (don't use the standard rand/mt_rand). – Vilx- Nov 20 '13 at 16:34
0

Just converting a ID into something like HEX might not give you the result what you like. Moreover its still easy 'guessable'

I would a a extra ID column (i.e. order_id). Set a unqi. index. Then on_creation use one of the following mysql functions:

SHA1(contcat('ORDER', id))
MD5(contcat('ORDER', id))
SHA1(contcat('ORDER', id, customer_id))
MD5(contcat('ORDER', id, customer_id))

UUID()

// try this in your mysql console
SELECT UUID(), SHA(CONCAT('ORDER',10)), SHA1(1);

You could (as in the example), add a simple text prefix like 'order'. Or even combine them. However i think UUID() would be easiest.

Implementation depends a bit on what you prefer you could use a stored procedure) or incorporate it in your model.

Roger
  • 7,535
  • 5
  • 41
  • 63