Basically, I am writing e-mails that are sent when a person registers on my website, and it will have an activation link that needs to have a string assigned to a GET variable.
When the person clicks the link, they will be taken to a page on the site where the string will then be decrypted and matched to something in the database that is unique to them. It will then activate their account.
I'm doing this in CakePHP, so if there's any function built in, that would be preferable.
I've tried lots of options, and most of them either are really short, really strange, or have characters in them that would mess up the GET variable.
I need the output to be preferably around 20 characters, with only letters and numbers.
Simply use a random, unique string. No need for encrypting or decrypting it, it just needs to be unique, long and random. That's simply known as an opaque token. It does not have any meaning, it's just something unique that only one user is supposed to have. UUIDs are a great fit.
do {
$token = str_replace('-', '', String::uuid());
} while (!$this->User->isUnique(array('token' => $token)));
Related
I'm sending a mass email though Emma (3rd party vendor) that will contain a link to a landing page. The landing page will be personalized and display some of the user's identifying info (name, title, email). Additionally, there will be a form collecting a few of the user's preferences that will be saved back to that user's record in Emma's database.
The user ID column in the 3rd party's database is incremental so I obviously can't just append that value through the query string otherwise user 522, for example, would get a link such as www.example.com?landing/?uid=522 allowing him (or anyone with the link)cto take a wild guess at other values for uid (such as 523... or 444) and change other users' preferences as well as view their personal data quite easily.
Bottom line is that I'm trying to find a secure way to pass an ID (or other unique value) that I can look up via API and use to dynamically display and then resubmit personal info/data on this landing page on a user-to-user basis.
I had an idea to add a custom column to my list in Emma for a unique identifier. I would then write a script (accessing Emma's API) to BASE64 Encode the ID (or possibly email address, as that would be unique as well) and add that to the list for each user. In my email, I could then pass that to the landing page in for the form of ?xy=ZGF2ZUBidWRvbmsuY29t, but I know this is encoding and not encrypting so not all that secure... or secure at all for that matter.
To my knowledge, there's no remote risk of anyone receiving the mailing having the ability and/or inclination to know what those extra characters in the link are, BASE64 Decode, BASE64 ENCODE another email address or integer an make a request with the newly BASE64 encoded value in order to manipulate my system in an an unintended way.
BUT for the purpose of this question, I'd like to know the "right" way to do this or what levels of security are currently being taken in similar circumstances. I've read about JWT tokens and some OOth stuff, but I'm not quite sure that's possible given that I've got the Emma API to deal with as well... and/or if that is overkill.
What is appropriate/standard for passing values to a page that are in turn used for a form to be resubmitted along with other user-supplied values when giving the user the ability to submit a "compromised" (intentionally or not) form could, at worst, could cause one of their competitors to have bad preference and opt-in saved data in our Emma mailing list?
Security on the web is all about "acceptable risk". You can reduce risk in various ways, but ultimately there's always some risk exposure you must be willing to accept.
Your very best option would be to force users to be logged-in to view the page, and to avoid using any querystring parameters. That way the backend for the page can pull the ID (or whatever it might need) out of the server's session.
Your next best option still involves forcing the user to be logged in, but leave the uid in the URL -- just be sure to validate that the user has access to the uid (i.e. don't let a user access another user's info).
If you can't do that... then you could create random keys/ids that you store in a database, and use those values (rather than uid or email or real data) in the URL. BUT let's be clear: this isn't secure, as it's technically possible to guess/deduce the scheme.
Absolutely DO NOT try passing the info in the URL as base64 encoded data, that's likely to be the first thing a hacker will figure out.
Keep in mind that any unsecured API that returns PII of any kind will be abused by automated tools... not just a user farting around with your form.
To my knowledge, there's no remote risk of anyone receiving the
mailing having the ability and/or inclination to know
^ That's always always always a bad assumption. Even if the result is at worst something you think is trivial, it opens the door for escalation attacks and literally exposes the company to risks it likely doesn't want to accept.
If you're stuck between bad options, my professional advice is to have a meeting where you record the minutes (either video, or in a document) and have someone with "authority" approve the approach you take.
In case anyone needs a working example, I found this at https://bhoover.com/using-php-openssl_encrypt-openssl_decrypt-encrypt-decrypt-data/. It uses PHP's openssl_encrypt and openssl_decrypt, and it seems to work perfectly for my purposes
<?php
$key = base64_encode(openssl_random_pseudo_bytes(32));
function my_encrypt($data, $key) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// Generate an initialization vector
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
// Encrypt the data using AES 256 encryption in CBC mode using our encryption key and initialization vector.
$encrypted = openssl_encrypt($data, 'aes-256-cbc', $encryption_key, 0, $iv);
// The $iv is just as important as the key for decrypting, so save it with our encrypted data using a unique separator (::)
return base64_encode($encrypted . '::' . $iv);
}
function my_decrypt($data, $key) {
// Remove the base64 encoding from our key
$encryption_key = base64_decode($key);
// To decrypt, split the encrypted data from our IV - our unique separator used was "::"
list($encrypted_data, $iv) = explode('::', base64_decode($data), 2);
return openssl_decrypt($encrypted_data, 'aes-256-cbc', $encryption_key, 0, $iv);
}
I first ran my_encrypt in a loop to encrypt the uid of each member in the list.
$members[$uid] = array('unique-identifier' => my_encrypt($uid, $key));
Next, through the API, I modified each member's record with the new value.
$ret = update_members_batch($members);
That only had to be done once.
Now in my email, I can pass the uid through the query string like this www.example.com/landing/?UID=<% unique-identifier %>, which will look something like www.example.com/landing/?UID= XXXXX2ovR2xrVmorbjlMMklYd0RNSDNPMUp0dmVLNVBaZmd3TDYyTjBFMjRkejVHRjVkSEhEQmlYaXVIcGxVczo6Dm3HmE3IxGRO1HkLijQTNg==
And in my page, I'll decrypt the query string value and use it via the API to get the email address with something like:
$member_email = get_member(my_decrypt($_GET['UID']))['email'];
and display it in the appropriate location(s) on my page.
I think this covers all my bases, but I am going to have a stakeholder meeting to get sign-off. What potential vulnerabilities does this expose that I should warn them about?
Okay, so I've been trying to fix the xss on my website, all times failed.
There is string called 'name' in whitch people change their name to something like
<script>alert("HahS");</script> I already tried
name = name.replace(/(<|>)/g, '');
if(name.indexOf('<') !== -1){
return false;
}
All I managed to do, is fix xss on the the user who sent the xss, but for all other people logged it still pops up.
Don't write your own regex for this. Use encodeURIComponent(), which is the proper way to escape user input like this.
const sanitized = encodeURIComponent(name);
The trick is that you need to do this in the right place. If your name parameter is being broadcast to other clients, you need to decide where you want to escape it. Generally, it is best to do this right before the data is rendered or used in such a way that would be dangerous if not escaped.
This implies:
Doing it on the client, rather than on your server.
Doing it when the client initializes / renders, rather than when the potentially malicious user submits their name.
If you are having a hard time figuring out where the right place is, just add encodeURIComponent() everywhere you use name and work backwards (figure out where you can remove it). You will probably end up with name being double-escaped in the mean time. But that's okay for most innocent values of name and it is a better default than being open to XSS attacks.
More details:
http://lukeplant.me.uk/blog/posts/why-escape-on-input-is-a-bad-idea/
https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet
Actually, i have this url http://mydomain.fr/user/1 in my web application. I think it is not very safe
I would hide the id which is auto_increment.
To not be able to do that:
http://mydomain.fr/user/1
http://mydomain.fr/user/2
http://mydomain.fr/user/3
http://mydomain.fr/user/4
http://mydomain.fr/user/[...]
I do not know which technique to use...
Hash MD5 stored beside primary key
UUID / GUID
I use MySQL.
You should restrict access to URLs based on authentication. Just making it 'hard to guess' an ID will not prevent someone from accessing another user's page or, e.g., deleting an unexpected user. Basically, anyone will be able to access any URL unless you provide some access control.
I think generate a random unique string for a user is the best way.
simply use sha1 hash should be ok.
There is no way properly to hide it, you can generate unique ID with a long random hashed string, it's harder to guest. Basically that won't prevent someone to access other's ID.
OP may be concerned with divulging the primary keys because it could leak information into how many of a certain resource exists.
For example, if he is building a web app and someone creates an account and sees a url of domain.fr/user/23 they will know they have created an account on an application with low adoption.
My suggestion would be to either use a GUID value as suggested above or a username that is constrained to be unique.
If you use a GUID, it will look ugly, but make sure to not just use the beginning part as you could greatly increase the chance of collision since the first 60 bits are based on the timestamp.
If you use a unique username, your url would instead look like domain.fr/user/username
I know this is easily done on RoR.
A container is identified with the label JA1234. This container should always go to destination A.
Another container is identified with the label 1234. The vast majority of containers are labeled this way and these always go to destination B.
(Note: The pool of containers constantly fluctates so we can't maintain a master list.)
The users can either scan/key in the container identifier. Many of the containers aren't barcoded so they need to type in the number. When it gets typed in the prefix 'JA' gets ignored and suddenly the programs error checks fail (allowing wrong destinations).
To prevent entry and to force barcoding I would like to require the program to scan a barcode. The only way to get the users to scan the barcode consistently is the provide a barcode in a gibberish (ie hexadecimal) format.
Is there a any built-in .NET framework feature that would convert the readable string into something unreadable that would require scanning? It would need to be reversible.
It sounds like you want the users to input the whole string always and you users are ignoring part of the string. To solve this you want the users to just use the barcode scanner.
But you really have three choices.
Only print out the barcode. They can't type what they can't see. However this is bad because if a barcode is damaged you won't be able to fallback to user entry
Encode it using something like System.Convert.ToBase64String. This is bad because then you'll have to print values like SkExMjM0 and MTIzNA== for JA1234 and 1234 which is easy to mistype when the users needs to type.
Use a check digit and append it to the string. You can then reject codes incorrectly entered or incorrectly read by the barcode scanner. The downside is there's nothing built in that can directly convert "JA2134" and you have to create your own check digit function.
An email confirmation gets sent when my website users making a booking. I have been requested to add a "Having trouble viewing this email?" link to the top which links to the email on the website.
I'm having trouble wondering how I should generate a link so the user could view this email.
Note that I am using a third party booking system which gives me a confirmation code such as: 12345BE913913 where 12345 is the property and BE is always BE and 913913 is a secondary number.
I'm wondering if I could just hash this number and make that the link? Eg sha1('12345BE913913') which turns into 070bae598f481351e24975d6509fc0a73cad9a17
And then the link in the email becomes something like href="http://blah.com/email/view/070bae598f481351e24975d6509fc0a73cad9a17
Question #1: Is this a pretty standard, secure way of doing it?
If so, I have one other concern... I would need to pull in this information in order to generate the email in my email/view. The web service only accepts the confirmation code, so I would have to feed the original one, 12345BE913913 to it. So I can't simply grab all the confirmation codes, sha1 them all and see which one equates to 070bae598f481351e24975d6509fc0a73cad9a17.
Question #2: Is my only option to get the booking information through the webservice that accepts the original confirmation code, to create a local database storing all the confirmation codes, and then get all of them SHA1'd and see if it equates to 070bae598f481351e24975d6509fc0a73cad9a17 to pull it up? It's not safe to use the actual confirmation code in the email, is it?
Why not pass both the confirmation number (as the primary key) and a MAC associated with it (to prevent people from guessing URLs.
URL Generation Pseudocode:
$mac = HMAC_SHA1($server_secret, $confCode);
$url = "http://$baseURL?conf=$confCode&m=$mac";
Email Display Pseudocode:
$mac = getParam("m");
$confCode = getParam("conf");
$expectedMac = HMAC_SHA1($server_secret, $confCode);
if($mac != $expectedMac) { # Or in real perl, ne instead of !=
return errorPage();
}
return email($confCode);
Why use SHA1 if you need the operation to be reversible? Why not instead encrypt it using a symetric algorithm such as Twofish to generate the URL. You can decrypt it on the server side using your key to recover the original confirmation code, then send the confirmation code to the 3rd party booking system. Since nobody else has your key, nobody else can recover the confirmation code.
Hashing the real identifier, and storing the hash as the key in a table to the original value for "reverse lookup" is a conventional approach.
That isn't the only option, however. You could encrypt the confirmation code. Since the confirmation codes are short and, and (I'm presuming) unique, it would be alright to use ECB mode with a block cipher, which would keep the resulting cipher text short (16 bytes instead of SHA-1's 20 bytes).
The caveat with ECB is that the same confirmation code will always produce the same cipher text. Most likely, a code is only sent in a single email; but, if it is sent more than once, an attacker would be able to determine that the email relates to the same confirmation code (but they wouldn't be able to determine the confirmation code itself).
I'm not sure what you mean by "safe". What can someone do with the confirmation number? Would they be able to use the hash to get the confirmation number from your site? Unless you use S/MIME (or PGP), an email is not private; assume an attacker can read email.