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.
Related
I'd like to store user id's in google analytics however since they have a policy of not allowing this, I want to encrypt some text in php and be able to decrypt when needed(The User Id).
The catch is I'd like it to be as short as possible. How can I achieve this. I'm not worried at all for this to be secure. Just need to mask it some fashion.
The simple and most seure solution to encrypting URL parameters is to not encrypt them at all. Instead use a randomly generated unique value, store it in another column in the desired database table, and select based on that.
For example:
function generateToken()
{
return strtr('+/', '-_', base64_encode(random_bytes(9)));
}
And when retrieving:
$data = $db->prepare("SELECT * FROM users WHERE selector = ?")->execute([
$_GET['user_token']
]);
I am using the following function to encrypt a string ($str) using a key ($key) to make a unique key.
Sample Code:
<?php
$key = "####";
$str = "123456789";
$encrypted_key = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_256, md5($key), $str, MCRYPT_MODE_CBC, md5(md5($key))));
echo $encrypted_key; // 3rfmDKb/Ig5FuUnkY8fiHpqA3FD4PflXMksJw+6WAns=
?>
The function is returning values consisting special characters including '+' . I am storing this values in database as a unique ID.
However in certain conditions, I need to pass the $encrypted_key through URLs . i.e; for using it with RESFful web services
Sample URL:
www.example.com/index.php?encrypted_key=3rfmDKb/Ig5FuUnkY8fiHpqA3FD4PflXMksJw+6WAns=
But this when requested through URL will decode '+' into 'spaces'
Code:
echo $encrypted_key = $_REQUEST['encrypted_key'];
// 3rfmDKb/Ig5FuUnkY8fiHpqA3FD4PflXMksJw 6WAns=
This conversion is further affecting the DB checks :
'3rfmDKb/Ig5FuUnkY8fiHpqA3FD4PflXMksJw 6WAns=' against '3rfmDKb/Ig5FuUnkY8fiHpqA3FD4PflXMksJw+6WAns='
Also I am having a concern of storing these encrypted values into indexed MySQL DB columns.
What should be the best practice to be adopted here? Any advise will be highly appreciated.
This answer only addresses the representation, not the likely-to-be-wrong use of crypto.
When you build objects that have special representation rules like database queries, paths in URLs, HTML code, JS code, and so on, you must ensure that you perform the proper kind of encoding of the values so that they roundtrip without harm.
For database query parameters, do not use string concatenation. Use prepared statements and placeholders.
For URLs, use the proper URL encoding function or an URL builder to construct your URL, do not blindly concatenate strings.
First, is not a good idea to use encrypted values as Unique ID or as Conditional Field, because they will change for the same value. This is very commom in encryption. If an encryption algorithm don't change the result for the same entry, it is not a good encryption.
Second, I had the same problem to deal with encryption and URL, and in my case a made my own encryption algorithm, using only valid characters for URL.
It is not dificult to implement an encryption: I used the ASCII code, one simple key, one simple math function, and nothing more. To decryption, I "reversed" the math function.
As in php we use $_GET to pass variables in the url , i want to pass variables which include the id of the user which i want to be anonymous, so can something be done which can encrypt the variable before passing it and the the variable once taken on the page can be decrypted to get the original variable value.
for eg:
Before passing variable $id=10;
Passed in the url as $id=dasfgjg;
when taken from the url and decrypted $id=10;
How can this be achieved?
You can use an RC4 cipher if you intend to encrypt/decrypt only on the server-side
http://www.phpkode.com/source/s/rc4-cipher-0-1/rc4-cipher-0-1/RC4.php
$my_secret_key = '3klmsd94mms.saeo44o!!3le';
if( isset($_GET['p']) ) {
$id = RC4::decrypt($my_secret_key, $_GET['p']);
// ....
}
else {
echo 'Go to the page';
}
Just generate random strings (make sure it's unique) for each record in the database and save it there, too. Then use this as an identifier. Note that, of course, this has nothing to do with encryption.
A quick and dirty way to achieve this (for each request)
on the client, create a string like 'xx:10:yy' where xx and yy are strings consisting pf random characetrs
on the client, create a salted hash of the users salted/hashed password
use this hash as a key and the string from the first bullet as cleartext for encryption with e.g. crypt.js
in the request send the encrypted string and the salt
on the server use the transmited salt and the users salted/hashed password to recover the key
on the server use mcrypt or friends to decrypt the string
on the server use standard PHP text processing functions to recover the payload from the decrypted string
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.
I need to create a simple hashing method for passing some data in a URL. It doesn't need to be very secure, it just shouldn't be obvious to most people.
The hash needs to contains the numerical id of the sender and the id of the recipient and I should be able to decode the data after reading the appended hash.
Any ideas? I'd like the hash to be a short as possible, simply because this url is meant to be shared via IM, email, etc..
Hash is one way only. If you want to decrypt it, you have to encrypt it. Try mcrypt with one of these.
For non secure stuff you can try base64_encode, You can also base_convert each numeric id from 10 to 36 digits or so. Multiplying the numbers with a secret constant could also help.
$obscurity = base_convert($recipientId * 42, 10, 36) . ':' . base_convert($senderId * 42, 10, 36)
Try base64 encoding/decoding. put together with the apache option "Multiviews" or apache mod_rewrite, would make your urls look like:
http://mysite.com/messages/[encoded string here]
base64 definitely should do the trick if you want to decode it again.
Note that this is not a 'hash', a hash generally means one-way encryption.
$senderId = 1234;
$recipientId = 5678;
$myString = $senderId . ":" . $recipientId;
echo base64_encode($myString);
You could use encryption, as mentioned by #OIS.
Or you could use a hash and store the hash values in a database keyed to sender id and recipient id. PHP has md5() and sha1() built in.