How to hide query parameters from URL sent in email from shell script - linux

I'm writing a shell script that will send email which has a link for application. When the user click link on the email, it will open the application. How to hide query parameters in the URL that was sent from shell script?

It doesn't make sense to hide URL / query string in any way; since if the user can open the URL, then the user must be able to read the URL. When you show that you have something to hide, curious users WILL try to find what and why are you hiding things.
sensitive information (confidential)
If what you're doing is sending something sensitive (ex: password), then just don't send it via email. Instead send some one-time-use token that expire after a day (assuming your email recipient will always read your email in a day). Format of such token can be JWT (see https://jwt.io for reference). For example:
instead of:
click here to login: https://yourapp.com/login?username=user&pass=user123
use these:
click here to login: https://yourapp.com/login?username=user&token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE1MTY0MzkwMjJ9.WkfqJZ3t0basNIqnRJD8R720BXmULhEONNlpHVxgoqA
Make sure you signed the JWT with a private key that only you know.
This way you're not exposing user's password just to prove that you're that user. The JWT in my example only contains claim that bearer of this token is named "user", and the token only valid from "iat" to "exp" field. (try copy-pasting the JWT above to jwt.io or decode it yourself).
If you're using this approach, you must also tell the users not to share their URL since other may impersonate them if they do.
identity
If what you're doing is sending something to prove who the user is, and it would be bad to be seen by other people because if they see it they can impersonate the recepient user, then you can send some hashed salt of the user's username or id. For example:
instead of:
click here to login: https://yourapp.com/login?username=user
use these:
click here to login: https://yourapp.com/login?username=3f480e2ceaf459b732f07b1b60a7ea16d1cd3244ef390ade01f401ad7b170445e08fb0b3583bb66210efc0fbe747c730f36f962de3cac07e6a165a80cb84def6
The hash in my example is sha512 digest of user some long long salt here 123 some long long salt here 123 some long long salt here 123 some long long salt here 123 some long long salt here 123 some long long salt here 123 some long long salt here 123 some long long salt here 123 some long long salt here 123
When your application read those hash, it can do a query to it's database like SELECT ... WHERE $urlparam = SHA512( CONCAT( username , '$salt' ) )
Same with above approach, you must also tell the users not to share their URL since other may impersonate them if they do.
long / large data
If what you're doing is sending very long/large data, and/or you do not want the user to change that data (ex: shopping cart data). Then you have to save it into database instead, and only send primary key / id of the record in URL. For example:
instead of:
click here to checkout: https://yourapp.com/checkout?username=user&cart1=myproduct1&cart1qty=20&cart2=myproduct2&cart2qty=50&cart3=myproduct3&cart3qty=99&cart4=myproduct4&cart4qty=1...(still continue)
use these:
click here to checkout: https://yourapp.com/checkout?cartid=1372384
This way, even if the user knows that his/her cartid is 1372384, it doesn't means anything for him/her. Of course this approach should be combined with some means of authenticating the user (otherwise some malicious user can see other's cart by entering random number or by guessing it)
lazy answer
If you just want to hide the URL no matter why, just use some strong encryption algorithm like AES256 to encrypt all query parameter, and then encode it as base64 (or url safe base64). In this case, your application (that read/handle the URL) and your script that generates the URL will have to share a same encryption key.
You will probably end up with something like: click here to do something: https://yourapp.com/handle?ciphertext=ZTZKU1RFUmZqNTZrSWszeWYwaEtiM0RWVzRRNWlFcE1lalpCbWFBM3Z5TEY1R0xYdGprVDRsSmk5Nzc4Y3dXamJ4RUpLYmRxZ2RXS0lLM01oMUc1U0t2dUFlWktldERoVHdzcjl6SS9CMmZuRmtoQnBMVmtEeFFhaXhIUHZwVFM2MlQ0SURRZEpMNzZ0bUptN2p3M0VuT0tMME15RW8xcG5ZSW5hcnpFUm1qSlBWWEhEYVNMcU42VFd5WFdDWmFsQXJaWDNpT0xrMWhpNFN2aXI3RjE4dz09

Related

How to pass a unique user ID to a page with user-specific, personal data

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?

Custom Password field on Employee Record

We have built an integration between Netsuite and our external system. We want to store the external system's credentials on the employee record but I'm having a really difficult time understanding how the password field type is supposed to work.
The documentation states "When validating, you pull the encrypted password value into a hidden field and use custom code to encrypt the value the user typed and compare it with the actual encrypted value."
But aside from this I don't feel like I have a solid idea on what the proper implementation should be.
So essentially I ended up doing what prasun did. One thing I did do what still create a password type field and add a client script on record save to encrypt the entered password and insert the value into another plain text field that was not displayed on the UI. This had the desired user experience to fulfill my requirements (showing the standard password field). From there, we just decrypted the encrypted field (the one we copied our password into and encrypted) during run time. So it's a little bit of a round about trip but it is working and in no place is the password being stored in plain text which was our desired goal!
or you can create a password fiels in plain text and can hide that field.The security level is low but still we can implement it with less effort.

What is GenerateEmailConfirmationToken() doing exactly?

I have two questions concerning ASP.Identity 2.0 "GenerateEmailConfirmationToken/GenerateEmailConfirmationTokenAsync" methods.
// Generate token
var token = Url.Encode(await UserManager.GenerateEmailConfirmationTokenAsync(user.Id));
Is this token stored in the database? I guess it should. But in which field? I just find "PasswordHash" and "SecurityStamp" on the User table. Both don't seem to match.
I was under the impression that once I generate an email token, the EmailConfirmed field of the User table would be set to false. But it stays true. So, what is the purpose of creating a token if the corresponding user account stays confirmed? Or in other words: What do I need to do in order to generate a new token AND also set the account to NOT confirmed?
To summarise the discussion in comments: tokens are not stored anywhere - they are crypto-generated (not exactly sure about exact process of generation) from SecruityStamp and when they are coming back, they can be de-crypted and compared.
As for EmailConfirmed field - this is for you to maintain and look after. You'll manually need to deny login for users with no confirmed email. And you'll need to set the flag when email confirmation does come through.

Coldfusion's cfloginuser-Tag: Why is the password required as argument?

Why is the password-argument required for cfloginuser-tag and what is it used for? I don't know the clear password in my application, because I'm using password-hash & salt to identify a user at login.
Documentation
You really don't need to know the clear password. If you have already executed the logic to login the user, you should already have the hashed password, simply use that. Or, you could even use createUUID() as the password attribute (this is better as the value stored with cflogin will have nothing to do with the user at all).
Point is, it does not matter what you use as ColdFusion does not use that value for anything. I would, however, caution against using the password the user types in, its never a good idea to store the raw, unhashed password anywhere.
I agree. It doesn't make much sense. I do use hash & salt technique too. In the past, I've just set the password attribute of the cfloginuser tag to the value the user has typed in for their password.

How should I generate a unique link per booking so my users can see a standalone email?

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.

Resources