In a CMS app I occasionally need to open an iframe of another domain. At the moment I am setting the URL for that iframe to something very obscure. Like http://domain.com/iframe/jhghjg34787386/. This works but theoretically that iframe source url will get saved in the user's history and could be accessed from the outside world.
So, I am wondering about using a time-based approach to an ever-changing hash or string that is processed on the request side and is checked on the iframe source side. However I would like it to be time based.
I could do this to get my hash:
<?php
$seed = '123456789'; // a password that both the parent and source have
$string = md5(time().$seed);
?>
But then the two servers have to be exactly synced. Any way to make the time constraint more fuzzy?
I am also open to other approaches. Is there any way to validate that the parent window for an iframe is of a certain domain?
You could add a key to your hash and send the timestamp with the query, e.g.:
$key = "YOUR_SECRET_KEY";
$time = time();
$hash = hash_hmac('sha256', $time, $key);
$url = "https://example.com/iframe?hash=$hash&time=$time";
On the other side you should first check if the timestamp is in the limits (e.g. not older than five minutes) and than rehash with the key and the submitted timestamp. If you get the same hash the request is valid.
Notes:
don't use MD5: the algorithm is completely broken and doesn't provide any security anymore (although it's supposed to still be ok when used with an HMAC…)
you should use hash_equals for comparing hashes to prevent timing attacks
we use an HMAC to guarantee data integrity and authentication. See https://crypto.stackexchange.com/questions/1070/why-is-hkx-not-a-secure-mac-construction for why we mustn't just concatenate time & key
You shouldn't use plain MD5 that; MD5 is not designed for ensuring message authenticity. Instead you can just give the timestamp publicly, alongside with other information (message), base64 encoded, so that it does not contain the ':' character. Then you can calculate the HMAC code of the message for example with
$hmac = hash_hmac("md5", $message, $secret)
$signed_message = $message . ":" . $hmac
On the other end you can then check this signature, by first splitting with ":", getting $message and $hmac, then you can check authenticity with
$hmac == hash_hmac("md5", $message, $secret)
If codes match, then check if the timestamp in the $message is still within the limits.
Be careful of using MD5 for hashing - it is cryptographically broken. There are any number of online sites to help create collisions. Rather use something like SHA256 and always include a long salting string.
If the user does not have to interact with the site in the iframe you could consider scraping the site code and inserting it directly into your code. There are a number of libraries available for this.
What about using something like
$hash = hash ( "sha256" , date("h") . 'myverylongsaltstring' );
So long as the servers have their timezones correct and are synchronized to within an hour this approach will work like your time() hash.
Additionally you could use something like TinyUrl to obfuscate the link a little further. Something along the lines of http://www.technabled.com/2008/12/create-your-own-tinyurl-with-php-and.html
If it is time based, then the amount of possible keys that a person would have to guess would be tiny.
Since I would know approximately when a URl might be generated, and I know how you are hashing it, then I can just create hundreds of thousands of links and test them out.
You should use UUID or something equivalent. The probability of a collission would be essentially impossible.
Related
I am working on a php login system and I was wondering how it is possible to make a secure password entry system that asks for say the 1st, 2nd and 8th characters of your password, like many online banking systems do. How could one make this and have the passwords stored as double-salted hashes?
An idea that comes into my mind is to store every character of the password hashed on separate field or serialized:
PSEUDOCODE:
$password is the user password, $secret_word is the word you use to check single characters
function get_hashed_characters($password, $secret_word) {
$char_store = ""
for every character $char in $secret_word
$hashed_char = some_hash_function($char + $password)
$hash_store = $char_store + $hashed_char
return $hash_store
}
function check_hashed_char($password, $hash_store, $char_index, $char) {
if len($hash_store) < $char_index * $HASH_LEN + $HASH_LEN return false
$hashed_char = substr($hash_store, $char_index * $HASH_LEN, $HASH_LEN)
return true if $hashed_char is equals to some_hash_function($char + $password), false otherwise
}
UPDATE: as C4ud3x pointed out, I hash both the character I want to store concatenated with the password
This is a completely different solution sometimes used on Linux systems: Challenge-Response authentication
HSBC use this style of password system on their website:
and in their app: )*
After contacting them they said:
Ensuring the security of our systems is and will continue to be our number one priority.
All the details that are sent to and from our systems are encrypted
using high encryption levels. As long as you keep your log on
information secret, we can assure you that the service is secure. As
you will appreciate, we cannot provide further details about the
additional security measures used by Online Banking, as we must
protect the integrity of the system.
Though this could just be a reference to their use of SSL, I think it probably suggests they:
Encrypt password data in the database
Encrypt the passwords in the database and then encrypt the whole database for better security.
I think the best solution is that provided by ColOfAbRiX, as it doesn't require encryption (which without the technologies available to banks such as HSBC is probably not very secure).
I'm using md5(uniqid()) to generate a unique hash for my token hidden input on my forms (like Log in, Sign up, Settings, etc) for my File Sharing and Hosting service and for the user session, so I can compare those two after the form is submitted.
But I'm wondering if md5(uniqid()) is good enough after I've read that md5 has lots of security flaws.
Are there better or more secure ways of generating tokens for my forms?
Output example
<input type="hidden" name="token" value="4c1dd84d3458964ee6d59c728dc70160">
This token should just be an unpredictable code. The best you can do to get such an unpredictable code with a deterministic computer, is to generate a really random number.
When you use the MD5 function with your uniqid, it does not add any randomness/unpredictability to your token, you (mis)use it as an encoder. The same goal you get with using the bin2hex() function, that's what MD5 does by default after calculating the binary hash. That said, the MD5 function is not unsafe here but has no advantage neither.
The more important point is, that the function uniqid() is not unpredictable, it is based on the current timestamp. This is the unsafe part in your code. To get an unpredictable number you can use the function mcrypt_create_iv() which reads from the random source of the operating system.
I would recommend to let PHP create the session token for you, with the session_start() function. If you really have reasons not to use a normal PHP session, then use mcrypt_create_iv() together with an encoding function like bin2hex() or base64_encode().
EDIT:
From your comments i see that this token is not used to maintain the session, instead to mitigate csrf. In this case of course the session_start function won't help (the session id should not be used as csrf token), but creating an unpredictable token is still important. This is an example of how this can be done:
/**
* Generates a random string of a given length, using the random source of
* the operating system. The string contains only characters of this
* alphabet: +/0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
* #param int $length Number of characters the string should have.
* #return string A random base64 encoded string.
*/
protected static function generateRandomBase64String($length)
{
if (!defined('MCRYPT_DEV_URANDOM')) throw new Exception('The MCRYPT_DEV_URANDOM source is required (PHP 5.3).');
// Generate random bytes, using the operating system's random source.
// Since PHP 5.3 this also uses the random source on a Windows server.
// Unlike /dev/random, the /dev/urandom does not block the server, if
// there is not enough entropy available.
$binaryLength = (int)($length * 3 / 4 + 1);
$randomBinaryString = mcrypt_create_iv($binaryLength, MCRYPT_DEV_URANDOM);
$randomBase64String = base64_encode($randomBinaryString);
return substr($randomBase64String, 0, $length);
}
There was a great answer to the first half of this on
https://security.stackexchange.com/a/19710 , the second half god a good answer from the manpage, as posted by rmcfrazier: http://www.php.net/manual/en/function.uniqid.php
quote combinatorics:
"There are devastating collision attacks on MD5. (...) In contrast, SHA1 appears to be much more secure. While there are some known attacks on SHA1, they are much less serious than the attacks on MD5. For this reason, SHA1 is a much better choice than MD5 in many settings." <- MD5
"Warning: This function does not create random nor unpredictable strings. This function must not be used for security purposes. Use a cryptographically secure random function/generator and cryptographically secure hash functions to create unpredictable secure IDs." <- uniqid
Per the man page, this should not be used for cryptographically secure tokens
http://www.php.net/manual/en/function.uniqid.php
You should use openssl_random_pseudo_bytes for your tokens
http://www.php.net/manual/en/function.openssl-random-pseudo-bytes.php
It really depends on how secure you need the token to be. Using the PHP session ID should suffice unless you need to verify the integrity of the key after submission.
Eg. PHP session id:
php > session_start();
php > echo session_id();
ritig5ecgp6ebmnq8p5imbdhl3
However, you can always generate more secure IDs using the hash() function eg:
http://us2.php.net/manual/en/function.hash.php
Don't use RipeMD
echo hash('ripemd160', 'The quick brown fox jumped over the lazy dog.');
ec457d0a974c48d5685a7efa03d137dc8bbde7e3
Unless the token needs to be cryptographically secure, the session key should suffice.
I am developing a system for Online Hotel booking system which I did not start from scratch. The information of customers(bookers) in the system are encrypted using MD5 but unlike normal md5() php function the system is quite complicated as you can see here :
$psw = md5("vhdsxnjuobef");
$t_cred_num = md5_encrypt($t_cred_num, $psw, 16);
and for Decryption it goes like :
$psw = md5("vhdsxnjuobef");
$t_credit_num = md5_decrypt($t_cred_num, $psw, 16);
this code is not Working though on my Server and there is alot of Customer's information Encrypted.
Example of hash of t_cred_num variable =>
fdRucZHctr7vIX+U400xGHq53Qemze0YQH1sAUjvmaC1P+XaRadI9CaX0wrkDXu6
Any Ideas on how to Decrypt these hashes ? When I use md5_decrypt with the hashes nothing happens.
I think the md5_crypt and md5_encrypt are functions hand crafted by the previous developer. md5 isn't supposed to be decryptable. Hash's are supposed to be one way functions: http://en.wikipedia.org/wiki/Hash_function
So, you'll need to find the definition of those functions. A search for "function md5_" in the code files should find the place in the code where they are defined.
there's no way to decrypt an md5. there are two things you can do:
if you have other account or know the password of another account then copy the md5 characters of it and place it on yours.
search for an md5 of let's say "admin". Paste the md5 equivalent of the word "admin" on your account. You can also use other words.
After that, login to your account then change back your password
I often want to redirect the user or email them a link but I want to mask the parameters in the URL so they can't tell what extra information is being sent.
For example, if I want to present a link to http://www.example.com/directory/ but I also want to pass extra parameters of an email address and a hash for someone:
Email: someone#example.com
Hash: 22sd359d5823ddg4653dfgfFSG2
I can send them to this link, but I don't want them to see the parameters:
http://www.example.com/directory/someone%40example.com/22sd359d5823ddg4653dfgfFSG2
So my first thought is just to base64_encode() it, but then you get those stupid == symbols at the end for the extra bytes. And also base64 encoding also generates quite long strings.
Is there an easier, URL-friendly way to encode a string to hide its contents?
How would you normally do this? Is base64_encode() a standard practice?
You could generate a short id and store what it's suppose to do in the database. So using
http://www.example.com/directory/K2SP26
for example would store the person's email address in the database along with where they are supposed to go. Check out http://kevin.vanzonneveld.net/techblog/article/create_short_ids_with_php_like_youtube_or_tinyurl/
Using something like base64_encode(), gzcompress, etc. to encode the string isn't a good way to obfuscate it since it's trivial to decode it. And yes, another option is to store the value in the database and just pass a key as people have suggested. But assuming you don't want to bother with that, what you really should do is to actually encrypt it using a private key, and then decrypt it on the other end with the same key.
For example:
function obfuscateString($s)
{
$secretHash = "BA2EC9E717B68176902FF355C23DB6D10D421F93EAF9EE8E74C374A7B0588461";
return openssl_encrypt($s, 'AES-256-CBC', $secretHash, 0, '1234567890123456');
}
function unobfuscateString($s)
{
$secretHash = "BA2EC9E717B68176902FF355C23DB6D10D421F93EAF9EE8E74C374A7B0588461";
return openssl_decrypt($s, 'AES-256-CBC', $secretHash, 0, '1234567890123456');
}
(Requires PHP version >= 5.3.0.) Replace the $secretHash with your own secret hex string.
Note: The initialization vector ('1234567890123456') is just a filler string in this example, but that's ok. You could come up with a way to use a unique initialization vector, but it isn't important for the purposes of obfuscating the URL parameters in most cases.
Probably a silly answer but why not use the mcrypt functions to hide your parameters from at least the more casual users?
If your redirect is triggered by PHP, I suppose storing the data in a session would be the obvious choice.
<?php
function redirectTo($url, $data) {
session_start();
$hash = md5(uniqid("rediredt", true));
$_SESSION[$hash] = array(
'my' => 'data',
'is' => 'invisible',
'to' => 'the user',
);
$delim = strpos($url, '?') ? '&' : '?';
$url .= $delim . 'redirection-key=' . $hash;
// might want to send 301 / 302 header…
header('Location: ' . $url);
exit; // might want to avoid exit if you're running fcgid or similar
}
function isRedirected() {
if (empty($_GET['redirection-key'])) {
return null;
}
if (!isset($_SESSION[$_GET['redirection-key']])) {
return array();
}
$t = $_SESSION[$_GET['redirection-key']];
unset($_SESSION[$_GET['redirection-key']]);
return $t;
}
might help you grasp the idea…
You have one of two choices:
The email address is hashed, and can therefore be decoded by a savy user.
-or-
The email address is stored on your server (database) and the email link contains only the database primary ID.
If you want the most secure method, that also gives the prettiest URLs, use method 2. If you absolutely do not want to store the email address in your database, then either use a common hash, such as base64 or even rot13, or roll your own. You will find that rolling your own is not simply, but it will stop most casual users from trying to peek inside the hash.
Its a bit hacky, but if you send them a page containing:
<form id="getme" action="directory/someone" method="POST">
<input type="hidden" name="email" value="someone#example.com">
</form>
<script> document.getElementById("getme").sumbit();</script>
As soon as that loads (assuming they have javascript enabled), they will be redirected where you want them with no url dirtyness.
I was wondering how mobile authenticators work (like Battle.net, rift, some banks have one, etc.), so I can make one for my own site (just for fun).
I understand the basics: authenticator has code related to the phone and code related to the website. Users enters the phone code on the website. Can then generate a token related (using the phone and website code).
I'm just wondering how the tokens are created. Is there a standard algorithm for this? How does the algorithm work? Any existing PHP libraries that can do something like this (as an example)?
Have a look at Google Authenticator. There are already iPhone, Android and Blackberry apps for that and it's an established protocol.
They have implemented it as an open-source PAM module which you may be able to use with the PECL PAM package.
There is a pure PHP version but I haven't used that so can't vouch for it.
The spec isn't that complex so you could probably implement it yourself, especially if you converted the C module. The specification linked there explains its working in full detail.
Edit: I guess to answer the original question, that's an RFC, so it's somewhat standardised, and it's a fully open specification and the tools to use it are fully open-source. The protocols are known as HOTP and TOTP. The former is HMAC based on a counter (so the nth password is used) whereas the latter is time-based (so the password cycles every 30 seconds).
Concerning the Blizzad Battle.Net authenticator, you can find an open source implementation in PHP : https://github.com/krtek4/php-bma
The implementation is used to provide a online authentication service for Battle.Net : https://authenticator.me
If you want to do something like it for your website, it's pretty simple. The only thing to share between the server and client part are the secret generated by the server. So when a client is requesting for a new secret, just store it and you will be able to compute the code at any moment to compare with what is sent to you.
I implemented this once. I use a 4 digit key with a subset of characters (notice that potentially confusing characters like 0oO and l1L are removed. I used 4 characters because the potential space of 4 digits from the characters set was larger than the 6 digits of an RSA key.
Anyway, I let the user log in with their username and password. If that is correct, generate a key and send it to the phone and save it in the session and show the user the next page, which requires the key be entered. The user gets the 4 digit key from their phone and enters it into the page. Then check what they entered against the session-saved key and there you go.
Some handy features to have: make the key expire after a few minutes, but long enough that text message delays don't make it impossible. Make it expire after a few bad tries. Give the users a link to resend the key or to send a new key.
//pick a random 4 digit string
$chars = "abcdefghjkrstwxyzABCDEFGHJKRSTWXYZ23456789";
$key = "";
for($i=0;$i<4;$i++){
//here, rand is used, but any generator could be used
//to choose the characters.
$key .= $chars[rand(0,strlen($chars)-1)];
}
//save it to the session
$_SESSION['test']['KEY'] = $key;
If it were me I'd go with generating a hash based on the previously used hash and a common nonce, the tricky bit would be keeping the two systems in sync. e.g.
<?php
class otp {
var $salt;
var $previous_hash;
var $user_id;
function __construct($user_id)
{
$this->user_id=$user_id;
list($this->$salt, $this->$previous_hash)
=unserialize(file_get_contents(BASE_PATH . $user_id));
}
function authenticate($submitted_otp)
{
if (!$this->salt) {
// user does not exist
return false;
}
$new_hash=$this->previous_hash;
// allow for the sequence to get out of sync by 5 steps....
for ($x=0; $x<5; $x++) {
$new_hash=md5($this->salt, $new_hash);
if ($new_hash===$submitted_otp) {
$this->update_token($new_hash);
return true;
}
}
// none of the next N iterations of the local password match
return false;
}
function create_user($user_id, $salt, $init_hash)
{
return file_put_contents(BASE_PATH . $user_id, array($salt, $init_hash));
}
function update_token($new_hash)
{
file_put_contents(BASE_PATH . $user_id, array($this->salt, $new_hash));
}
}
Of course, in practice you probably wouldn't want to use a whole 32 char md5 hash (just, say, the first 6 characters, and applying cleansing such as changing 'S' to '5' etc).