What is GenerateEmailConfirmationToken() doing exactly? - asp.net-identity-2

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.

Related

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

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

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?

(Nintex) Query User Profile returns missing values

Working in Nintex, I have a workflow that is kicked off when a form is saved. The workflow generates a PDF version of the Nintex form (we need actual signatures - digital in the future, fingers crossed). This is done by updating a word template, then converting to PDF.
When the word document is updated, the 'assigned to:' field is getting an AD account name instead of the user's First, Last name (which is expected). Now I'm trying to format it so it's more appropriate to the Hard Copy (in the Nintex form, it shows as Last, First).
I used a Query User Profile action, passed it the same variable I was using to hold the form's 'assigned to' value, and then used the drop down menu to choose the user profile variables I wanted (First, Last, also: username, account name, Distinguish name). All values are generating empty strings.
I've incrementally tried handing the values to variables that are of type String, Person, or Collection. I also handed it my username instead of the variable and set my account info for the login. I've always selected values, so I don't think it's a typo.
I'm at a loss... the workflow emails the user at the end, so it's getting the data. I hope that's enough info, I'm new to SP/Nintex so it could be a rookie mistake. Any help is appreciated.
Thank you!
I've seen issues where the User Profile Service properties have not been configured adequately, which leads to a lot of empty variables when running an LDAP Query action for AD attributes.
If this is the case, then one approach is to get the Farm admin (if you don't have access) to take a look at Manage User Profiles in Central Admin and see what AD attributes are mapped to the user profile. If mappings are changed you'll need to run a full sync to bring over the values. It can be inconsistent in my experience as well.

Zend form: secure way store entry ID when editing?

I'm new to the Zend Framework and I have a problem to create an edit form with the Zend_Form.
My problem is that I need to store the entry ID during editing, I've seen some examples that are using a hidden form field, but a hidden field can be manipulated by a user.
So: how can I set a form field which gets populated by $form->populate($data); and is available after submiting the request but is not editabel/visible to the user in any way?
Thanks for any help!
I'm not sure if there's really a point in trying to hide the value.
Consider the following:
To display the correct editor form, you need the ID of the object that is to be edited.
Before allowing the user to edit a certain ID, you would check if the user can edit it or not.
Thus, if you put the ID in the form, it shouldn't really matter:
When you POST the edit form, you should again check that the user can still edit the ID.
If the user changes the hidden ID, it doesn't really matter. They could still go and edit the other ID by finding it on the site. (This is assuming your check didn't tell you the user does not have access)
what kind of data you wanna hide?
data should be in post or get.if you dont put your data in your form,then you will have to use GET which is less secure than POST.
If you have some data and you dont want the user to see those data,then you should not put those data in a form.you can store and retrieve hidden data using forms submitted values.lets suppose your hidden field is users password.you dont need to send password back to the client when client is editing the form.you can manipulate password in your controller according to the user`s submitted first name and last name.
If you still insist, you may wanna try encrypting data using ZF and echo ing your value and setting encrypted data into a hidden form element.
Zend_Form generates an HTML form element with the form elements you specify. So its element capabilities are narrowed to a simple HTML form.
The hidden form element is used to pass those data that the user is not supposed to enter by hand. But as you yourself said it, there is no guaranty it could not be tampered. so no security is provided by using a hidden form value.
Most of times you'd better use server side values (like stored in sessions) to reference to values that are to be protected from user.
I suggest you keep the ID in a session value, and then you could use the session key in the hidden form field. this way the user can not change the target ID. However you are not able to use the $form->populate($values) on this in one step. you would have to set the target value with other steps:
fetch data from the session
set the form element value with the fetched data

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