I am building an online-and-mailout ballot system in PHP/Laravel that requires an access token that is to be typed in by a person at some point, either the actual user or an administrator. As such, I would assume that the token would have to be generated and not traceable back to the original user via database dumps.
What would be the best way to generate such a code? I have looked at Hashids, but unsure if that would be a suitable solution, unless it is fine to use the ballot creation timestamp in seconds as a second value to use along with the ballot ID.
Create a random byte array with a CSPRNG such as openssl_random_pseudo_bytes and then Base58 encode the result. This will give the shortest human usable character string for level of unique-ness.
Base58 a very human usable character set without easily confused characters. One example of this usage is Bitcoin.
Here is the character set:
123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz
One of the Best and one of the fastest is:
$token = bin2hex(openssl_random_pseudo_bytes(int));
binary safe and cryptographically secure, but if You want get with length wanted, You must cut this and then You will get easer to hook token made only from 16 characters. If You want get human reliable string You must implement it by self and this is long way. I made something like this using cryptographically secure extensions SafeToken.
Related
We were making kind of a simple game,
in which:
Users receive the next number of play as an encrypted string Before they play
After they play, the encryption password is provided to them to check the play number was correct.
Each encrypted string is only valid for 1-2 hours and number of play , verificating string and encrypted string is regenerated again after that time
The encrypted string includes a verification (5 char) code so both users and we can make sure Decryption process was successful
Sample Character to get Encrypted (QQ9LU is random verification code provided to user before the play):
Next Play Number: 8 - Verify String: QQ9LU
Sample Encrypted String (provided to user before play):
NXRykKOv3B6kuu4Ke3svp7HH3enNiqIZrJSXJiF54QkHHjtXgqpUXxyuP7YUNICeFLg==
Sample Password (provided after play):
Please note this is generated randomly for each encryption
FA00RDjA77hlOzcOzH6kuGcc29CyM7Hw
We use CodeIgniter 2.2.2 Encryption Class to encrypt/decrypt strings
Encryption Method Info:
Function Used: $this->encrypt->encode($msg, $pass); with random pass each time
Cipher is CodeIgniter 2 default MCRYPT_RIJNDAEL_256
Mcrypt mode is MCRYPT_MODE_CBC
My Questions are:
Can i trust that users cannot break the encrypted string (and know the number of play before they get the password) in 1-2 hours (aside from getting lucky)
Is placing random verification code Verify String: T3YH4 in there good or bad? does is affect security? (this is to verify decryption result was successful, also we added it because the only variable in each string was a single digit, for example only number 8 changes to 7, so we wanted to add more variable characters to the string to possibly have a better security)
Any other suggestion is appreciated
Short answers:
From a technical POV, what you're doing is unsafe, although it might be enough for just a 2-hour timeframe.
What you're trying to do here is called "message authentication", but that's not how it should be done, which in turn does impact security. You should use a HMAC instead.
My advice would be to upgrade to CodeIgniter 3 (CI2 will stop receiving even security updates in a few months) as soon as possible, and use its new Encryption library instead. That will make it safe for years, not hours.
Long answer:
The encryption library should do both encryption and authentication for you, but unfortunately the CI_Encrypt class itself is badly written and lacking a lot of functionality (such as authentication), which is why it was DEPRECATED and is being replaced by a new (CI_Encryption) library in CodeIgniter 3.
Explaining all the flaws in here would be quite the task, so I'd rather link you to an external article (not self-promoting, don't worry), which does that quite nicely if you're interested in the low-level details.
No matter which library you use however, one thing must be noted - a password is not the same thing as an encryption key.
Passwords have a varying length and are used by humans, which means that they must be readable by humans, and that in turn limits them to a certain set of characters.
Encryption keys on the other hand have a fixed length (each encryption algorithm is designed to work with a specific key length; for Rijndael-256 that's 32 bytes, which you seem to match) and are not limited to human-readable characters (which means more entropy and therefore more security) - they represent raw binary data.
Anything else can be controlled (and therefore automatically done) by a library, but if you pass a password instead of a key - that's what the library will use, so you should take care of that.
The best and simple way to do that is to use the filesystem functions to create a simple text file for each user in non public path with two lines, the first of them is a unique random string (long string varied in length) and the second is the number.
Then using sha1_file get the hash value of the file then store it in the database related to its path and creating time, then send this hash to the user.
After the user has played, check the value by another script that get the value of the hash from the database, then read the file and parse its second line to display the number.
By this way, you have gave the user a hash not for a string, but it for a file and cracking it to get the file back is not simple as to be done in two hours.
You are giving your Encryption/Decryption logic to client side. Hacker will easily identify how your password and encryption strings are being match.
Many framework have their own password creationg and compare logics. Yii using SALT and other features like SHA1 etc...
Keep it simple and keep all things at your end. Generate your encryption things and store at your end. Follow simple steps,
Generate encryption password (using SALT and/or other encryption tools) and store at your end
Ask client (user) to enter their password (key) and get at server side
Convert your password (key) to encryption password and compare
CPasswordHelper will be helpful for you. Try to download Yii source code and put out their logic for you.
Hope that helps !!
Sounds like a fun game!
I am assuming you are creating these strings in files on a filesystem. If you were hosting them on some web application that would assume different techniques to break the string.
Adding a code to the end of the string is called salting the string. While this makes the string harder to guess, if you are adding a hardcoded salt instead of a randomly generated salt it can still be easily broken by brute force methods.
I would try using a one-way hashed string for the password and storing that in a database. The user is unable to decrypt the string and has to just provide a matching password to gain access to your string. It is possible for programs to break one-way hashed strings but I find it unlikely someone will be smart enough to do that if they are in college and only have two hours. It takes alot of domain knowledge and experience to start generating one-way hashed strings to brute force it.
In addition you are probably safe with the method you are doing currently, students will not likely be able to break a string in 2 hours unless they are familiar with advanced encryption hacking scripts that take some work to find. I am guessing they will do trial and error, using different decryption libraries similar to the example you provide and hoping they get lucky with the library of strings they are trying to match against yours.
Also information is important with any type of encryption. Telling someone you are adding a 5 code salt to your string, will give them some insight into how your encryption algorithm works. They can then try methods of breaking it based on the information you give them. Try the same thing with your own algorithm and leave the students in the dark, I doubt anyone will break anything in the time alotted. Alot of hacking techniques involve going through an information gathering process where the hacker scopes out or maps a system before trying to attack it.
I'm making a website which people can publish posts. In my database each post has an ID like 1, 2, 3 etc. but I would like to change them, like using a hash like Youtube does.
For example instead of http://localhost/post/1
They would go to http://localhost/post/hu9NA827z
Is there a method like hashing the numbers and decoding it?
Well, while you could encrypt/decrypt it doesn't make much sense (you're gonna make it slower without any real benefit).
Waht you can do is to have the primary key in your DB to be a string and generate a hash for the id or add a new column with a unique index, save the hash there and search the posts by the hash column (and maybe keep the id for internal purposes). You can use complex algorithms or just md5(uniqid()), since this is not for security i wouldn't worry too much. Make sure that when creating a new post, the uniqueness is not being violated. Now you have another reason for an insertion to fail (the hash not being unique) so prepare for that.
Check:
http://php.net/manual/en/function.md5.php
http://php.net/manual/en/function.uniqid.php
Since there is no need for this hash to be secure, you can just use the PHP built-in hash function, md5(). I suggest using the timestamp as input:
$id = md5(time());
Just truncate it to make it shorter. I suggest you keep the original primary key an autoincrement integer and add this hash as a new column.
The sequence hu9NA827z is BASE64. Decoding it, you get a binary sequence of 6 bytes.
For instance:
base64_encode('123456') // = 'MTIzNDU2'
base64_decode('MTIzNDU2') // = '123456'
However, on YouTube, BASE64 is not being used to protect the information, its purpuse is just to serialize it into a human-readable ASCII format. The real message behind it is a 48-bit binary sequence.
This binary sequence is probably the encrypted version of what would be the video ID on a database, but what it really is only YouTube developers knows for sure and they certainly expect it to remain that way.
In your case, you could simply implement a similar system using one of the many two-way encryption methods offered in PHP like MCrypt that supports a lot of encryption algorithms of your choice including the very safe AES.
I need to create truly unique token when inserting records in CakePHP. The table can contain millions of rows so I cant just base on some randomly generated strings. I do not want to use a microtime() as well, because there is, though very small probability that two records can be submitted exactly at the same moment.
Of course the best solution would be to use String::uuid(), but as from cakephp documentation
The uuid method is used to generate unique identifiers as per RFC 4122. The uuid is a 128bit string in the format of 485fc381-e790-47a3-9794-1337c0a8fe68.
So, as far as I understood it does not use cake's security salt for its generation. So, I decided to hash it by security component's hash function (or Auth Password function), because I need it to be unique and very, really very secure at the same time. But then I found the question, saying that it is not a good idea, but for php uniqid and md5.
Why is MD5'ing a UUID not a good idea?
And, also I think the string hashed by security component is much harder to guess - because, for example String::uuid() in for loop has an output like this
for ($i = 0; $i < 30; $i++) {
echo String::uuid()."<br>";
}
die;
// outputs
51f3dcda-c4fc-4141-aaaf-1378654d2d93
51f3dcda-d9b0-4c20-8d03-1378654d2d93
51f3dcda-e7c0-4ddf-b808-1378654d2d93
51f3dcda-f508-4482-852d-1378654d2d93
51f3dcda-01ec-4f24-83b1-1378654d2d93
51f3dcda-1060-49d2-adc0-1378654d2d93
51f3dcda-1da8-4cfe-abe4-1378654d2d93
51f3dcda-2af0-42f7-81a0-1378654d2d93
51f3dcda-3838-4879-b2c9-1378654d2d93
51f3dcda-451c-465a-a644-1378654d2d93
51f3dcda-5264-44b0-a883-1378654d2d93
So, after all the some part of the string is similar, but in case of using hash function the results are pretty different
echo Security::hash('stackoverflow1');
echo "<br>";
echo Security::hash('stackoverflow2');
die;
// outputs
e9a3fcb74b9a03c7a7ab8731053ab9fe5d2fe6bd
b1f95bdbef28db16f8d4f912391c22310ba3c2c2
So, the question is, can I after all hash the uuid() in Cake? Or what is the best secure way to get truly unique and hashed (better according to my security salt) secure token.
UPDATE
Saying secure token, I mean how difficult it is for guessing. UUID is really unique, but from the example above, some records have some similarity. But hashed results do not.
Thanks !!
I don't think you need to worry about the UUIDs overlapping.
To put these numbers into perspective, the annual risk of someone being hit by a meteorite is estimated to be one chance in 17 billion,[38] which means the probability is about 0.00000000006 (6 × 10−11), equivalent to the odds of creating a few tens of trillions of UUIDs in a year and having one duplicate. In other words, only after generating 1 billion UUIDs every second for the next 100 years, the probability of creating just one duplicate would be about 50%. Or, to put it another way, the probability of one duplicate would be about 50% if every person on earth owns 600 million UUIDs.
http://en.wikipedia.org/wiki/Universally_unique_identifier#Random_UUID_probability_of_duplicates
Continue to use String::uuid() and rest easy :)
A UUID is unique
I need to create truly unique token when inserting records in cakphp
That is exactly what a UUID is. It is normally used in distributed systems to prevent collisions (multiple sources inserting data, possibly out of sync, into a datasource).
A UUID is not a security measure
I need it to be unique and very, really very secure at the same time
Not sure in what way hashing a uuid is supposed to enhance security - it won't. Relying on security by obscurity is more or less guaranteed to fail.
If your need is random tokens of some form - use a hash function (Hashing a uuid is simply hashing a random seed), if you need guaranteed-unique identifiers use UUIDs. They aren't the same thing and a UUID is a very poor mechanism of generating random, non-sequential "un-guessable" (or whatever the purpose is) strings.
Generating a random string suitable for cryptographic purposes was answered well here:
Secure random number generation in PHP
The code sample fills the string $pr_bits with random binary data, so the characters are unprintable. To use this in a URL, you could convert the binary data to printable characters a couple ways. None of them enhance the security but make them ready for URLs.
convert bytes to hex: bin2hex($pr_bits)
convert bytes to base64: base64_encode($pr_bits)
hash the bytes (because the output is conveniently in hex, not for added security): string hash ('md5' , $pr_bits)
I include the last one because you will see people use hash functions for other reasons, like to guarantee the output is 16bytes/128bits for md5. In PHP people use it to convert a value into HEX.
I have come up with the following solution
to use a string as a result of concatenating current time in microseconds and random string's hash
$timeStr = str_replace("0.", "", microtime());
$timeStr = str_replace(" ", "", $timeStr);
echo Security::hash('random string').'_'.$timeStr;
// 5ffd3b852ccdd448809abb172e19bbb9c01a43a4_796473001379403705
So, the first part(hash) of the string will contribute for the unguessability of the token, and the second part will guarantee its uniquenes.
Hope, this will help someone.
Does anyone know how to generate a long (e.g. 280 characters) random string in PHP without having to use a for loop that will loop through characters 280 times? I need it in order to create a custom session ID.
The PHPSESSID is not secure enough in my opinion being too short and not too random. I know Facebook and Twitter, use long session IDs (150, 550 chars respectively).
There could be an option to use MD5 strings or Bcrypt encryption of different string such as PHPSESSID, host, User-Agent etc. but I'm not sure this is the right way of doing it.
If you're asking a question like that, it probably means you don't know anything about cryptography or security. Trying to generate a "long random string" because, as you say, "The PHPSESSID is not secure enough" will probably lead you to a custom and insecure implementation.
Generating a random string is IMPOSSIBLE, at least not with your current hardware: you may approximate a fair pseudorandom generator but that is only useful for educational purposes.
PHP's Session ID generation algorithm is fairly efficient; if you think it is not secure enough, then you'll likely waste time making it better. You may probably want to use a different authentication mechanism if you are looking at maximum security (using a client certificate for example).
If websites such as Twitter, Facebook, or another site with similar traffic use longer session IDs, it may be not because it is more secure (well in a way), but rather because it avoids conflicts.
Finally, if you want a longer session ID without trying to write your own algorithm, you should use the following PHP configuration directive:
session.hash_function which can take any hash algorithm known by PHP.
You may also want to use session.bits_per_characters to shorten or lengthen the string. Note that if you do this, the string may be longer or shorter, but the data remains the same -- only represented differently (base 16, base 32, etc.)
Additional info:
You may also increase the entropy by using a custom source (file) and setting the length of the seed:
ini_set("session.hash_function", "sha512");
ini_set("session.bits_per_charater", 4); // 4 means hex
ini_set("session.entropy_file", "/dev/urandom");
ini_set("session.entropy_length", "512");
Can it be binary hexadecimal?
bin2hex(mcrypt_create_iv(140, MCRYPT_DEV_URANDOM));
Or if you have access to hash_pbkdf2():
hash_pbkdf2('sha256', PHPSESSID, mt_rand(), 1, 280, false);
You'll probably find that not all the bits in a "long" session id are purely random - they might provide additional information to the application to enable rapid extraction of key information.
PHP session ids by default aren't too bad, and can be improved by providing some extra entropy (e.g. from /dev/urandom)
Look at php_session_create_id in ext/session/session.c in the php source
It goes like this:
get time of day
get remote ip address
build a string with the seconds and microseconds from the current time, along with the IP address
feed that into configured session hash function (either MD5 or SHA1)
if configured, feed some additional randomness from an entropy file
generate final hash value
So getting a duplicate or predicting a session id is pretty difficult.
Make yourself a long string and shuffle it.
$string = 'asdfhdfiadfshsdfDSGFADSFDSFDSFER524353452345';
$new_string = shuffle($string);
My PHP Application uses URLs like these:
http://domain.com/userid/120
http://domain.com/userid/121
The keys and the end of the URL are basically the primary key of the MySQL database table.
I don't want this increasing number to be public and I also don't want that someone will be able to crawl the user profiles just by interating the Id.
So I want to encrypt this Id for display in a way I can easily decrypt it again. The string shouldn't get much longer.
What's the best encryption method for this?
Simple Obscuring: Base64 encode them using base64_encode.
Now, your http://domain.com/userid/121 becomes: http://domain.com/userid/MTIx
Want more, do it again, add some letters around it.
Tough Obscuring: Use any encryption method using MCrypt library.
A better approach (from a usability and SEO perspective) would be to use a unique phrase rather than an obscured ID. In this instance the user's user name would seem an ideal solution, and would also be un-guessable.
That said, if you don't want to use this approach you could just use a hash (perhaps md5) of the user's user name which you'd store in the database along with their other details. As such, you can just do a direct lookup on that field. (i.e.: Having encrypt and decrypt part of the URL is probably overkill.)
You have a variety of choices here:
Generate and store an identifier in the database. It's good because you can then have readable keys that are guaranteed to be unique. It's bad because it causes a database schema change, and you have to actually query that table every time you want to generate a link.
Run an actual key-based encryption, for instance based on PHP's MCrypt. You have access to powerful cryptographic algorithms, but most secure algorithms tend to output strings that are much longer than what you expect. XOR does what you want, but it does not prevent accessing sequential values (and the key is pretty simple to determine, given the a priori knowledge about the numbers).
Run a hash-based verification: instead of using 121 as your identifier, use 121-a34df6 where a34df6 are the first six characters of the md5 (or other HMAC) of 121 and a secret key. Instead of decoding, you extract the 121 and recompute the six characters, to see if they match what the user sent. This does not hide the 121 (it's still right there before the hyphen) but without knowing the secret key, the visitor will not be able to generate the six characters to actually view the document numbered 121.
Use XOR with shuffling: shuffle the bits in the 30-bit identifier, then apply the XOR. This makes the XOR harder to identify because the shuffle pattern is also hidden.
Use XOR with on-demand keys: use fb37cde4-37b3 as your key, where the first part is the XOR of 121 and md5('37b3'.SECRET) (or another way of generating an XOR key based on 37b3 and a secret).
Don't use base64, it's easy to reverse engineer: if MTIx is 121, then MTIy is 122 ...
Ultimately, you will have to accept that your solution will not be secure: not only is it possible for users to leak valid urls (through their browser history, HTTP referer, or posting them on Twitter), but your requirement that the identifier fits in a small number of characters means a brute-force attack is possible (and becomes easier as you start having more documents).
Simplest but powerful encryption method: XOR with a secret Key. http://en.wikipedia.org/wiki/XOR_cipher
No practical performance degradation.
Base64 representation is not an encryption! It's another way to say the same.
Hope this helps.
Obscuring the URL will never secure it. It makes it harder to read, but not much harder to manipulate. You could use a hexadecimal number representation or something like that to obscure it. Those who can read hex can change your URL in a few seconds, anyway:
$hexId = dechex($id); // to hex
$id = hexdec($hexId); // from hex
I'd probably say it's better indeed to just create a random string for each user and store that in your database than to get one using hash. If you use a common hash, it's still very easy to iterate over all pages ;-)
I would write this in comments, but don't have the rep for it (yet?).
When user click on a link you should not use primary key, You can use the pkey in a session and get it from that session. Please do not use query string....
generate an unique string for each user and use it in your urls
http://domain.com/user/ofisdoifsdlfkjsdlfkj instead of http://domain.com/userid/121
you can use base64_encode and base64_decode function for encrypt and decrypt your URLS