I am using a system to sell tickets for a game.
Current id numbers for tickets are consecutive. EG: 651,652,653. If someone would buy a new ticket, his ticket id would be 654.
I am looking for a solution to inform the user about his ticket unique id by sending him a string instead of the ticket number, so that he can have a reference, but he would not know how many tickets were purchased before.
By encrypting ticket number 651 I want to generate a string having 6-7 characters maximum, all uppercase, and this algorithm should be two way and reversed only using a secret key.
EG: using a key like 881hu and encrypting number 651 I should obtain something like UTR8N1A0 .
I want uppercase letters for readability, and the length of the new string should not be too long, because then it would be hard to remember.
Do I have any options of such algorithm? I am using PHP as server language for all this.
I agree with #RaggaMuffin-420 comment, that way you can have 6-7 uppercase characters maximum. As for something like mcrypt there's no easy way to make it in 6-7 chars...
of course if you don't want to do something silly like:
$secret = 123456789;
$tickedID = 654;
$code = strtoupper( dechex($secret + $tickedID) );
echo 'Code is: ' . $code . '<br>';
$decode = hexdec($code) - $secret;
echo 'Decoded: ' . $decode;
Output:
Code is: 75BCFA3
Decoded: 654
Disclaimer: I would go with adding unique code in database referencing to the actual ticked id.
One option - you can calculate a hash(consecutive_number + salt) and then encode the hash.
Update: If you need to quickly extract the original consecutive_number, hide it inside the encoded hash string. You can read it, and you can verify it by re-calculating the hash (as the salt is secret, one won't be able to easily calculate the hash to construct a valid key).
There's a lot of bad advice floating around the Internet (and bad designs like "hashids"), but ultimately what you want is:
Don't do this:
Do this instead:
Images (and detailed arguments) from: The Comprehensive Guide to URL Parameter Encryption in PHP
Related
I'm building a login/reg system in CI3 and I have a doubt.
Instead of use:
$key=md5(uniqid());
for sending key for email activation...can I use another method? I don't know, using database (mysql)? hash with SHA1 maybe?
I don't want the most secure way, just a solution that is not fake as uniqid() is. 'Cause I know that id generated by uniqid() is not really random, so there is no reason to hash to md5 is simply...stupid.
Thank you in advance.
Actually email verification/password generation should be
easy to understand
easy to get back
It means if you send md5 what happen is user get an word length of 32. Its very bad sign.
MD5 example
8b1a9953c4611296a827abf8c47804d7
what I Suggest you is
In codeigniter there is an helper call String. Use that.
There is 5 different choices to pick you
alpha
alnum
basic
numeric
nozero
How to use this?
Load helper in controller
$this->load->helper('string');
Then just add this
random_string([$type [, $len]]) ;
Example :
random_string('alnum', 8);
How these diff with each other ??
Assume : length is 8 in these scenario.
alpha
This will just print String with UPPER Case
Example AKTHDOGK
alnum
This will print String with UPPER and LOWER Case
Example JdKsPeeU
basic
This will print random number using mt_rand() in php.
Example 12756079
numeric
This will print Numeric string.
Example 01234567 (Not used. So not sure)
nozero
This will print and number. But there is no zero
Example 12345678 (Not used. So not sure)
FYI: I never used numeric and nozero. So i have no idea about the example. But all other function are i used.
I'm trying to send a random number to the database for a user/article ID. It is currently using auto increment as a counting system. However, I'd like for the number to be random and unpredictable.
The mt_rand() function in PHP does exactly what I need. Although, my question is what happens when the function returns a number already in use. Of course I can just use a is_null() to check. But if it keeps on picking a number in use I could imagine that that'd slow the operation down.
Any thoughts on what I might be able to do to get around this? Perhaps I'm going at this all wrong.
Also if there's a function that gives letters and numbers that would also help greatly (like Youtube's).
Thanks for reading!
Here is a simple function to create a 10 character long string. The string is built using upper/lowercase text and numbers. Auto increment is definitely the way to go, however, if you are dead set, the function below should help.
<?php
function randomID()
{
$ID = substr(str_shuffle(str_repeat('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',5)),0,10);
echo $ID;
}
randomID();
?>
To make the string longer, change 10 to whatever you like. In terms of ensuring it does not already exist. I would suggest you generate the new ID and then do a search in the database to ensure it does not exist before inserting. Granted this is an extra step in the chain, but unfortunately this is what needs to be done.
Hope this helps
You should always use an auto_increment field as the primary key of your database. Not doing that costs you a great deal in performance. You can certainly create a secondary ID field with your random ID. I'd probably use a hashing function to get the best chance of a random string:
<?php $key = md5(rand(0,999).time().$myItemTitle); // ex. ce4075a3d3f6fd757eb6dd44810cbe14
You should always (in normal use cases) use an auto incremented ID for performance reasons. If you're purpose is to be able to somewhat hide the next post because someone could be guessing for it then you better add some kind of hashed unique field to your database.
Always random (just encrypting ms) :
<?php
$value = time();
$key = "543yretghf436436";
$encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $value, MCRYPT_MODE_ECB);
//if you want even long string change 128 to 256
$encrypted = base64_encode($encrypted);
$encrypted = rtrim($encrypted, '=');
echo $encrypted;
?>
e.g.
Egttu2XhRGdAiXVfszscWg
XlttfR3XaL6pym1uSNY7Kg
YvoKCweUnN8gZyodRYysLA
What you actually want is some "random" key to use as an identifier for the article. I would keep the auto_increment and eigther:
add an column with a "hashkey" or "random key" to identify the article. This poses the "i already have this key" issue (which should not be that large unless you have billions of articles). See some code examples already posted.
create an extra table with pregenerated keys (i.e. 10000 id -> key values) where you can lookup the id by key. If the table runs out you can easily generate new values. This way you don't have to worry about getting "slow" generation speed.
I need to generate a strong unique API key.
Can anyone suggest the best solution for this? I don't want to use rand() function to generate random characters. Is there an alternative solution?
As of PHP 7.0, you can use the random_bytes($length) method to generate a cryptographically-secure random string. This string is going to be in binary, so you'll want to encode it somehow. A straightforward way of doing this is with bin2hex($binaryString). This will give you a string $length * 2 bytes long, with $length * 8 bits of entropy to it.
You'll want $length to be high enough such that your key is effectively unguessable and that the chance of there being another key being generated with the same value is practically nil.
Putting this all together, you get this:
$key = bin2hex(random_bytes(32)); // 64 characters long
When you verify the API key, use only the first 32 characters to select the record from the database and then use hash_equals() to compare the API key as given by the user against what value you have stored. This helps protect against timing attacks. ParagonIE has an excellent write-up on this.
For an example of the checking logic:
$token = $request->bearerToken();
// Retrieve however works best for your situation,
// but it's critical that only the first 32 characters are used here.
$users = app('db')->table('users')->where('api_key', 'LIKE', substr($token, 0, 32) . '%')->get();
// $users should only have one record in it,
// but there is an extremely low chance that
// another record will share a prefix with it.
foreach ($users as $user) {
// Performs a constant-time comparison of strings,
// so you don't leak information about the token.
if (hash_equals($user->api_token, $token)) {
return $user;
}
}
return null;
Bonus: Slightly More Advanced Use With Base64 Encoding
Using Base64 encoding is preferable to hexadecimal for space reasons, but is slightly more complicated because each character encodes 6 bits (instead of 4 for hexadecimal), which can leave the encoded value with padding at the end.
To keep this answer from dragging on, I'll just put some suggestions for handling Base64 without their supporting arguments. Pick a $length greater than 32 that is divisible by both 3 and 2. I like 42, so we'll use that for $length. Base64 encodings are of length 4 * ceil($length / 3), so our $key will be 56 characters long. You can use the first 28 characters for selection from your storage, leaving another 28 characters on the end that are protected from leaking by timing attacks with hash_equals.
Bonus 2: Secure Key Storage
Ideally, you should be treating the key much like a password. This means that instead of using hash_equals to compare the full string, you should hash the remainder of the key like a password, store that separately than the first half of your key (which is in plain-text), use the first half for selection from your database and verify the latter half with password_verify.
using mcrypt:
<?php
$bytes = mcrypt_create_iv(4, MCRYPT_DEV_URANDOM);
$unpack = unpack("Nint", $bytes);
$id = $unpack['int'] & 0x7FFFFFFF;
PHP has uniqid function http://php.net/manual/en/function.uniqid.php with optional prefix and you can even add additional entropy to further avoid collision. But if you absolutely possitevily need something unique you should not use anything with randomness in it.
This is the best solution i found.
http://www.php.net/manual/en/function.uniqid.php#94959
I'm using this code:
$url = "http://www.webtoolkit.info/javascript-base64.html";
print base64_encode($url);
But the result is very long: "aHR0cDovL3d3dy53ZWJ0b29sa2l0LmluZm8vamF2YXNjcmlwdC1iYXNlNjQuaHRtbA=="
There is a way to transform long string to short encryption and to be able to transform?
for example:
new_encrypt("http://www.webtoolkit.info/javascript-base64.html")
Result: "431ASDFafk2"
encoding is not encrypting. If you're depending on this for security then you're in for a very nasty shock in the future.
Base 64 encoding is intended for converting data that's 8 bits wide into a format that can be sent over a communications channel that uses 6 or 7 bits without loss of data. As 6 bits is less than 8 bits the encoded string is obviously going to be longer than the original.
This q/a might have what you're looking for:
An efficient compression algorithm for short text strings
It actually links here:
http://github.com/antirez/smaz/tree/master
I did not test it, just found the links.
First off, base64 is an encoding standard and it is not meant to encrypt data, so don't use that. The reason your data is so much longer is that for every 6 bits in the input string, base64 will output 8 bits.
There is no form of encryption that will directly output a shortened string. The result will be just as long in the best case.
A solution to that problem would be to gzip your string and then encrypt it, but with your URL the added data for the zip format will still end up making your output longer than the input.
There are a many different algorithms for encrypting/decryption. You can take a look at the following documentation: http://www.php.net/manual/en/function.mcrypt-list-algorithms.php (this uses mcrypt with different algorithms).
...BUT, you can't force something to be really small (depends on the size you want). The encrypted string needs to have all the information available to be able to decrypt it. Anyways, a base64-string is not that long (compared with really secure salted hashes for example).
I don't see the problem.
Well... you could try using md5() or uniqid().
The first one generate the md5 hash of your string.
md5("http://www.webtoolkit.info/javascript-base64.html");
http://php.net/manual/en/function.md5.php
The second one generates a 13 unique id and then you can create a relation between your string and that id.
http://php.net/manual/en/function.uniqid.php
P.S. I'm not sure of what you want to achieve but these solutions will probably satisfy you.
You can be creative and just do some 'stuff' to encrypt the url so that it is not easy quess able but encode / decode able..
like reverse strings...
or have a random 3 letters, your string encoded with base64 or just replace letters for numbers or numbers for letters and then 3 more random letters.. once you know the recipe, you can do and undo it.
$keychars = "abcdefghijklmnopqrstuvwxyz0123456789";
$length = 2;
$randkey = "";
$randkey2 = "";
for ($i=0;$i<$length;$i++) $randkey .= substr($keychars, rand(1, strlen($keychars) ), 1);
I'm interested in creating tiny url like links. My idea was to simply store an incrementing identifier for every long url posted and then convert this id to it's base 36 variant, like the following in PHP:
$tinyurl = base_convert($id, 10, 36)
The problem here is that the result is guessable, while it has to be hard to guess what the next url is going to be, while still being short (tiny). Eg. atm if my last tinyurl was a1, the next one will be a2. This is a bad thing for me.
So, how would I make sure that the resulting tiny url is not as guessable but still short?
What you are asking for is a balance between reduction of information (URLs to their indexes in your database), and artificial increase of information (to create holes in your sequence).
You have to decide how important both is for you. Another question is whether you just do not want sequential URLs to be guessable, or have them sufficiently random to make guessing any valid URL difficult.
Basically, you want to declare n out of N valid ids. Choose N smaller to make the URLs shorter, and make n smaller to generate URLs that are difficult to guess. Make n and N larger to generate more URLs when the shorter ones are taken.
To assign the ids, you can just take any kind of random generator or hash function and cap this to your target range N. If you detect a collision, choose the next random value. If you have reached a count of n unique ids, you must increase the range of your ID set (n and N).
I would simply crc32 url
$url = 'http://www.google.com';
$tinyurl = hash('crc32', $url ); // db85f073
cons: constant 8 character long identifier
This is really cheap, but if the user doesn't know it's happening then it's not as guessable, but prefix and postfix the actual id with 2 or 3 random numbers/letters.
If I saw 9d2a1me3 I wouldn't guess that dm2a2dq2 was the next in the series.
Try Xor'ing the $id with some value, e.g. $id ^ 46418 - and to convert back to your original id you just perform the same Xor again i.e. $mungedId ^ 46418. Stack this together with your base_convert and perhaps some swapping of chars in the resultant string and it'll get quite tricky to guess a URL.
Another way would be to set the maximum number of characters for the URL (let's say it's n). You could then choose a random number between 1 and n!, which would be your permutation number.
On which new URL, you would increment the id and use the permutation number to associate the actual id that would be used. Finally, you would base 32 (or whatever) encode your URL. This would be perfectly random and perfectly reversible.
If you want an injective function, you can use any form of encryption. For instance:
<?php
$key = "my secret";
$enc = mcrypt_ecb (MCRYPT_3DES, $key, "42", MCRYPT_ENCRYPT);
$f = unpack("H*", $enc);
$value = reset($f);
var_dump($value); //string(16) "1399e6a37a6e9870"
To reverse:
$rf = pack("H*", $value);
$dec = rtrim(mcrypt_ecb (MCRYPT_3DES, $key, $rf, MCRYPT_DECRYPT), "\x00");
var_dump($dec); //string(2) "42"
This will not give you a number in base 32; it will give you the encrypted data with each byte converted to base 16 (i.e., the conversion is global). If you really need, you can trivially convert this to base 10 and then to base 32 with any library that supports big integers.
You can pre-define the 4-character codes in advance (all possible combinations), then randomize that list and store it in this random order in a data table. When you want a new value, just grab the first one off the top and remove it from the list. It's fast, no on-the-fly calculation, and guarantees pseudo-randomness to the end-user.
Hashids is an open-source library that generates short, unique, non-sequential, YouTube-like ids from one or many numbers. You can think of it as an algorithm to obfuscate numbers.
It converts numbers like 347 into strings like "yr8", or array like [27, 986] into "3kTMd". You can also decode those ids back. This is useful in bundling several parameters into one or simply using them as short UIDs.
Use it when you don't want to expose your database ids to the user.
It allows custom alphabet as well as salt, so ids are unique only to you.
Incremental input is mangled to stay unguessable.
There are no collisions because the method is based on integer to hex conversion.
It was written with the intent of placing created ids in visible places, like the URL. Therefore, the algorithm avoids generating most common English curse words.
Code example
$hashids = new Hashids();
$id = $hashids->encode(1, 2, 3); // o2fXhV
$numbers = $hashids->decode($id); // [1, 2, 3]
I ended up creating a md5 sum of the identifier, use the first 4 alphanumerics of it and if this is a duplicate simply increment the length until it is no longer a duplicate.
function idToTinyurl($id) {
$md5 = md5($id);
for ($i = 4; $i < strlen($md5); $i++) {
$possibleTinyurl = substr($md5, 0, $i);
$res = mysql_query("SELECT id FROM tabke WHERE tinyurl='".$possibleTinyurl."' LIMIT 1");
if (mysql_num_rows($res) == 0) return $possibleTinyurl;
}
return $md5;
}
Accepted relet's answer as it's lead me to this strategy.