2

Can anyone suggest a versatile PHP encrypt/decrypt algorithm that encrypts in the following way:

  1. it's fast
  2. it's short, similar to YouTube's video ids
  3. can be used as a valid id (an elements attribute)
  4. can be used as part of a URL safely

Security is not the primary concern here. I'm just wanting to prevent the casual "hacker" from easily accessing certain pages by changing the URL (e.g. www.domain.com/?id=1 can easily be changed to www.domain.com/?id=2).

StackOverflowNewbie
  • 39,403
  • 111
  • 277
  • 441
  • I suppose you're looking for something like ["using UUID instead of a simple id"](http://debuggable.com/posts/why-uuids:48c906cc-7a6c-4f22-9e20-6ffd4834cda3), but not encryption. – Alvin Wong Aug 03 '12 at 02:15
  • base64 won't work. For one, it tends to be long. Also, the characters is uses aren't valid for ids, etc (e.g. base64_encode(1) is MQ==). Finally, I'd like it not to be so obvious how it was encrypted (and thus making decryption so easy). – StackOverflowNewbie Aug 03 '12 at 02:16
  • build your own algorithm - so that you can enc/dec. That's not too hard – Yang Aug 03 '12 at 02:19
  • @metal_fan - that is exactly what I am asking help with. – StackOverflowNewbie Aug 03 '12 at 02:20
  • The Hashids library supports many different programming languages and allows you to specify a salt. http://hashids.org/php/ – John Kramlich Oct 04 '15 at 20:48

4 Answers4

3

Can't you use MySQL's built-in MD5 function?

You can use MD5 to hash the database id, then the URL will be something like

mysite.com/?id=2343423423j23kj3kkjdslfjsldjfsfjs

E.g.

$id = $_GET['id'];

And in MySQL

select * from product where md5(id) =  $id;
Max
  • 21,123
  • 5
  • 49
  • 71
cherankrish
  • 2,004
  • 1
  • 16
  • 11
2

If you really really really want to encrypt your primary key (Highly inefficient, will explain later) then use

$url = substr(md5(uniqid($row['id'], true)),0,6);

Where row['id'] is your primary key. This creates a url/html safe 6 character string, all will be unique (kind of, see below).

Now. This is why you should NOT do this.

  1. Encryptions should always take place in the backend when uploading data to the sql database, not client side. The general rule is less client side processing the better. It is the difference clientside from pulling $row['url'] from your sql database where $row['id'] is the key, or pulling the id then running an encryption. That adds 1 more step client-side.
  2. Although highly unlikely, using an encryption like the one below has the potential to have duplicates. (If your site has 1000+ keys your chances of a duplicate is higher) so to prevent a duplicate you would need to encrypt your key, then do an sql search to retreive ALL of your keys, encrypt EACH key, then compare EVERY key to the current encrypted key. That adds 4x(however many keys you have) to your processing time.
  3. Really it is just bad form. If forever reason you wanted to search for a page based on the encrypted url, you would have to again retrieve ALL keys and encrypt + compare all of them.

For everyone else USE THIS if you want efficiency

I have the script to create the unique id

$token = substr(md5(uniqid(rand(), true)),0,6); // creates a 6 digit token

I use a mysql database to store previously used id's, you could use any other kind of database to store the Id's.

function generateUniqueID () {
  $token = substr(md5(uniqid(rand(), true)),0,6); // creates a 6 digit token
  $query = "SELECT count(*) FROM table WHERE url = $token";
  $result = mysql_query($query, $connection) or die(mysql_error());
  $numResults = mysql_num_rows($result);
  if ($numResults) {
    generateUniqueID();
  }
}

Using this code you have ONE step client-side, to get the row where id then you receive the row['rl'].

Please read up on program efficiency and take a look at the documentation for mysql, do so and you will get more happy clients :)

Branden S. Smith
  • 1,161
  • 7
  • 13
  • I don't need a unique id. I already have that: my database's primary key. I want to encrypt that key, then decrypt it later. – StackOverflowNewbie Aug 03 '12 at 02:17
  • @StackOverflowNewbie we know you're not in your question. But we are suggesting an alternatives to what you want – Alvin Wong Aug 03 '12 at 02:18
  • 1
    If you need to decrypt it later then just use base64_encode() and base64_decode(). I don't see any practical use for this, it would be much better to have an incrementing key as your primary then have an encrypted id that you use for urls. – Branden S. Smith Aug 03 '12 at 02:22
  • 1
    Jeez I feel stupid, I've been doing it my way (see answer) for years. I never thought to store an encrypted ID in the db. Your way FTW. Pleased to be accepting my +1 :) – da5id Aug 03 '12 at 02:35
  • 1
    @BrandenStilgarSueper - base64_encode will not work because it uses characters that are not valid for HTML ids. Also, storing the encrypted id in the DB is no good since it's a computed value. Why encrypt the primary and then store it also? And even if I did that -- I still need to know how to encrypt the id in the first place, which is what my question is. – StackOverflowNewbie Aug 03 '12 at 07:49
  • @StackOverflowNewbie ...Ok lets start over then. Are you ID's numeric or strings? What is the purpose of encrypting/decrypting them? Your question above looks as though you just want id's that cant be looped through so not 1,2,3,4 etc. Is this correct? – Branden S. Smith Aug 03 '12 at 08:04
  • 1
    @BrandenStilgarSueper - I want to encrypt my primary keys (numeric, auto-increment) such that the result is safe to use as an HTML id. It needs to also be safe to use as part of the URL (without having to urlencode, etc.). – StackOverflowNewbie Aug 03 '12 at 12:04
  • the question asks for decryption as well, but the accepted answer only shows one-way hashing. No doubt this can be useful, but is certainly not a good answer. – Sanjay Verma Nov 15 '15 at 11:31
2

If it is not possibly to modify the database and add a new column to hold the "identifier", you could go with a block cipher which has a small block size.

Blowfish is something you could go with. You encrypt the id with a secret key and output it in hex format. This way you end up having 16-byte hex encoded identifiers (as long as the numeric id fits into Blowfish's block size).

Roughly something like (no validations included):

$key = md5('crypto key...', true); // For demonstration purpose

function encrypt($id, $key)
{
    $id = base_convert($id, 10, 36); // Save some space
    $data = mcrypt_encrypt(MCRYPT_BLOWFISH, $key, $id, 'ecb');
    $data = bin2hex($data);

    return $data;
}

function decrypt($encrypted_id, $key)
{
    $data = pack('H*', $encrypted_id); // Translate back to binary
    $data = mcrypt_decrypt(MCRYPT_BLOWFISH, $key, $data, 'ecb');
    $data = base_convert($data, 36, 10);

    return $data;
}

There are cryptographic shortcomings with this kind of approach, but assuming your id-numbers won't grow over 2821109907455 (and they are not negative), this should be fine. As long as 17 byte identifiers are o.k. for you (16 bytes of encrypted data from encrypt function and one byte of hard coded letter to make sure your html attributes start with a letter).

BenMorel
  • 34,448
  • 50
  • 182
  • 322
timoh
  • 211
  • 1
  • 4
  • I'm not looking to just encrypt/decrypt. The encrypted ids need to be HTML id and URL safe. I tried your encryption function and passed it "1". The result was 9ca8df3fea86ae5e. That's not a valid HTML id. – StackOverflowNewbie Aug 03 '12 at 12:02
  • Take a look at my answers second to last paragraph. In other words, you need to hard code a letter prefix. Like: a9ca8df3fea86ae5e (note the 'a'), and then just remove it before passing to decryption funtion. – timoh Aug 03 '12 at 12:14
  • Removed incorrect last paragraph, so my previous comment shoud read "Take a look at my answers last paragrap." – timoh Aug 03 '12 at 12:37
  • Just to clarify, this solution does work but ONLY for numeric ids, this will not encrypt/decrypt strings. – robmcvey Nov 18 '14 at 10:44
1

The uniqid() function could be what you're looking for if you need to generate the IDs themselves.

FtDRbwLXw6
  • 27,774
  • 13
  • 70
  • 107