ProtectedData.Unprotect on an IIS application - fails to work after IISRESET - c#

I need to store and retrieve sensitive data from a local database - this data is used by a web application.
In order to protect said data I've opted to make use of the ProtectedData class.
The IIS application is running using a specific AD user (Identity property in the Advanced Settings).
Everything works fine until I do an IISRESET - at this point, it seems that the identity is changed for the purposes of the ProtectedData class, and I'm left with data I cannot decrypt - I'm getting a Key not valid for use in specified state exception.
Here's the code I'm using:
static public string Encrypt(string data)
{
var encryptedData = ProtectedData.Protect(System.Text.Encoding.UTF8.GetBytes(data), entropy, DataProtectionScope.CurrentUser);
return Convert.ToBase64String(encryptedData);
}
static public string Decrypt(string base64string)
{
var encryptedData = Convert.FromBase64String(base64string);
return System.Text.Encoding.UTF8.GetString(ProtectedData.Unprotect(encryptedData, entropy, DataProtectionScope.CurrentUser));
}
The entropy is obviously static for my application.
What's going on? I was under the impression that DataProtectionScope.CurrentUser will use, as the name implies, the current user - which should be, to my knowledge, the application pool identity. Why does it seem like this is changed when I perform an IISRESET?

Whilst I don't know why this was happening, I changed the code to use AES encryption instead - this is working fine.
While not an answer to the problem per-say I think it's a valid workaround that deserves mentioning.
EDIT:
I think I've found what was causing the issue (I still don't exactly know WHY this is happening, but I did notice something today).
If the web application is using the ApplicationPool identity, then all is fine and well and DPAPI should continue working after an IISRESET. However if I change the identity to a specific user defined in AD, then things go haywire after the application pool is recycled.
Lucky for me In this particular case I neither need a specific AD user any more and the main encryption is based on AES (DPAPI can't be used to access a shared resource when load balancing comes into the equation) with DPAPI only being used to encrypt the local copy of the AES keys.

I had the exact error when using ASP.NET Core Data Protection API, and for those of you who has this error, please confirm that LoadUserProfile was enabled for the Application Pool User.

Related

WIF Key not valid for use in specified state

My web application is using WIF to authenticate users against their own STS (I have no control over how their STS is setup, I just give them the url to the federation metadata).
My web application is running over 2 load balancers with 2 servers behind them, I am also using sticky sessions with a 1hr timeout on them and both machines share the same machine key, I also have the LoadUserProfile set to true in IIS.
It seems to work fine when only 1 user is logged on using a unique STS but as soon as there are a more then one, I can see the following errors been logged on the server many times in a short period.
Key not valid for use in specified state.
Stack Trace: at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope) at Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Decode(Byte[] encoded)\r\n at System.Security.Cryptography.ProtectedData.Unprotect(Byte[] encryptedData, Byte[] optionalEntropy, DataProtectionScope scope) at Microsoft.IdentityModel.Web.ProtectedDataCookieTransform.Decode(Byte[] encoded)
How can I solve this error, or any help in diagnosing this issue?
For anyone looking for the same issue in the future, the information here fixed my issue. http://weblogs.asp.net/cibrax/the-system-cannot-find-the-file-specified-error-in-the-wif-fam-module (copy of the article below)
The Federation Authentication Module (FAM) shipped as part of WIF protects by the default the session cookies from being tampered with in passive scenarios using DPAPI. As I mentioned in the past, this technique simplifies a lot the initial deployment for the whole solution as nothing extra needs to configured, the automatically generated DPAPI key is used to protect the cookies, so this might be reason to have that as default protection mechanism in WSE, WCF and now WIF.
However, this technique has some serious drawbacks from my point of view that makes it useless for real enterprise scenarios.
If web application that relies on FAM for authenticating the users is hosted in IIS. The account running the IIS process needs to have a profile created in order to use DPAPI. A workaround for this is to log into the machine with that account to create the initial profile or run some script to do it automatically.
DPAPI is not suitable for web farm scenarios, as the machine key is used to protect the cookies. If the cookie is protected with one key, the following requests must be sent to the same machine. A workaround for this could be to use sticky sessions, so all the user requests from the same machine are handled by the same machine on the farm.
Fortunately, WIF already provides some built-in classes to replace this default mechanism by a protection mechanism based on RSA keys with X509 certificates.
The “SecuritySessionHandler” is the handler in WIF that is responsible for tracking the authentication sessions into a cookie. That handler receives by default some built-in classes that applies transformations to the cookie content, such as the DeflatCookieTransform and the ProtectedDataCookieTransform (for protecting the content with DPAPI). There are also two other CookieTransform derived classes that are not used at all, and becomes very handy to enable enterprise scenarios, the RSAEncryptionCookieTransform and RSASignatureCookieTransform classes. Both classes receives either a RSA key or X509 certificate that is used to encrypt or sign the cookie content.
Therefore, you can put the following code in the global.asax file to replace the default cookie transformations by the ones that use a X509 certificate.
protected void Application_Start(object sender, EventArgs e)
{
FederatedAuthentication.ServiceConfigurationCreated += new EventHandler<Microsoft.IdentityModel.Web.Configuration.ServiceConfigurationCreatedEventArgs>(FederatedAuthentication_ServiceConfigurationCreated);
}
void FederatedAuthentication_ServiceConfigurationCreated(object sender, Microsoft.IdentityModel.Web.Configuration.ServiceConfigurationCreatedEventArgs e)
{
var cookieProtectionCertificate = CertificateUtil.GetCertificate(StoreName.My,
StoreLocation.LocalMachine, "CN=myTestCert");
e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(
new SessionSecurityTokenHandler(new System.Collections.ObjectModel.ReadOnlyCollection<CookieTransform> (
new List<CookieTransform>
{
new DeflateCookieTransform(),
new RsaEncryptionCookieTransform(cookieProtectionCertificate),
new RsaSignatureCookieTransform(cookieProtectionCertificate)
})
));
}
The only part of the code you will need to change is where it tries to find your certificate location on your server.
var cookieProtectionCertificate = CertificateUtil.GetCertificate(StoreName.My, StoreLocation.LocalMachine, "CN=myTestCert");

How to hide a password using SmtpClient In ASP.NET MVC? [duplicate]

I have added the following settings inside my web.config file to initiate an API call to external system. So I am storing the API URL + username + password as follows:-
<appSettings>
<add key="ApiURL" value="https://...../servlets/AssetServlet" />
<add key="ApiUserName" value="tmsservice" />
<add key="ApiPassword" value="test2test2" />
Then inside my action method I will be referencing these values when building the web client as follows:-
public ActionResult Create(RackJoin rj, FormCollection formValues)
{
XmlDocument doc = new XmlDocument();
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
foreach (string key in formValues)
{
query[key] = this.Request.Form[key];
}
query["username"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiUserName"];
query["password"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiPassword"];
string apiurl = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiURL"];
But in this was I will be exposing the username and password and these can be captured by users, so my question is how I can secure the API username and password?
You can encrypt the web.config with aspnet_regiis. This is to stop people with access to your server from reading sensitive information.
By the way, I would put your config settings inside a class, that can then be injected into your controllers - it will make unit testing easier.
Generally, web.config is a secure file and IIS does not serve it, therefore it will not be exposed to users who are making requests to web server. Web server only serves specific type of files and web.config is surely not one of 'em.
Quite often you save your database connection string in it which includes password. Now imagine an scenario where web.config was not secure. You have created a major security threat to your application.
Therefore, specially as long as your project is not too big, you should not be worrying about it.
Yet, you may have a better approach but creating a project called "Resources" and save all the critical information such as settings, consts, enums and etc in there. That would be a slick and organized approach.
If you are passing the username/password over the wire though (for example in case of a secured API call), you may want to use https to make sure that information that are travelling are encrypted but that has nothing to do with the security of web.config file.
I know this may sound like over engineering, but if you can, you really should use a secret management service, such as AWS Secrets Manager or Azure Key Vault.
By storing your passwords, even in encrypted form, in your web.config or app.config, you creating several problems for yourself:
now your encrypted passwords are going to be committed to your source control, making them accessible to anyone with the read access. To get rid of them properly, you'll need to do a history rewrite (if you are using git) which is a major pain
while you technically can put your passwords in the machine-level config, and outside of the source control, you'll need to update all those files when your password changes
anyone who's got your encrypted password now can try to decrypt it, using either aspnet_regiis.exe tool (if that's what you used to encrypt it), or if you rolled your own security, reverse engineer that using your source code, figuring out what decryption algo & key it is using
whenever you need to change a password, you'll need to make changes to that file & re-deploy the application.
On the other hand, when you use a secret management service, no secrets or decryption key or algorithm is stored in your source code. Retrieving a secret is as simple as this:
For Azure Key Vault:
var keyVaultUrl = "https://<your-key-vault-name>.vault.azure.net/";
var credential = new DefaultAzureCredential();
var client = new SecretClient(vaultUri: new Uri(keyVaultUrl), credential);
KeyVaultSecret secret = client.GetSecret("<your-secret-name>");
Console.WriteLine($"{secret.Name}: {secret.Value}");
For AWS Secrets Manager (skipped some error handling code):
var client = new AmazonSecretsManagerClient(accessKeyId, secretAccessKey,
RegionEndpoint.APSoutheast2);
var request = new GetSecretValueRequest {
SecretId = secretName
};
GetSecretValueResponse response = null;
response = client.GetSecretValueAsync(request).Result;
This approach has lots of advantages over storage of local secrets:
you don't have to mess with storing different values in configs for Prod/Staging/Dev environments -- just read appropriately named secrets (such as '[Dev|Prod|Stag]DBPassword`
only selected few people can have access to the very important secrects (such as, I dunno, say Transfer all $$$ from Deus account to all E-Coin wallets everywhere authorisation code)
if anyone steals your source code (disgruntled employee, accidental leak) non of your passwords have been leaked
changing a password is easy -- you just update it using the could management console and restart the app(s)
I have written a couple of articles, showing how to set up and read secrets with AWS and Azure on my blog, feel free to check it out if you need step-by-step directions and complete source code:
How to use AWS Secrets Manager to store & read passwords in .Net Core apps
How to securely store and retrieve sensitive info in .NET Core apps with Azure Key Vault
The web.config file is just a file on the file system and so you should (mostly) consider its security in the same way as you would any other file. It will not be served by IIS (unless you make a frankly insane config change to IIS - easy to check for, just try and request it with a browser)
It is good practice to secure your website directory (c:/sites/my-site-here or whatever) using folder permissions to just the website app domain user to read the files (and the deployment user if needed) by using normal windows file permissions
So it may not be necessary to encrypt if you have confidence in the security of the web server. However if you are say on shared hosting, or selling the website code, or the source code is public, then it might be wise to encrypt it. it is a little bit hassle some though.
Why use Web.config?
One of the advantages of having data in Web.config as opposed to storing it in Constants, Enums, etc... is that you can change the data for different environments easily.
Protecting data
The best way to secure the data in Web.config is to encrypt them. Instead of encrypting entire sections in the config file as suggested by Joe and user1089766 you can just encrypt the password string and store it in the config.
Retrieving data
You can use a helper function such as the one below to decrypt the keys.
private const readonly string decryptKey = "";
public string GetFromConfig(string key)
{
var encryptedText = System.Web.Configuration.WebConfigurationManager.AppSettings[key];
var plainText = Decrypt(encryptedText, decryptKey);
return plainText;
}
In this way the decryption key will be inside the project common for all environments. But you can change the data in the web.config easily without recompiling your app.
PS: You can change the decryptionKey and the corresponding data with each version to improve security.
I have to encourage you to separate the code from the Keys. Even if you encrypt the file, someone can clone it from your repo and while it is encrypted, you may no longer be able to track the file, or may get access to a key in the future, etc.
Plus as Art indicated (and maybe others in the thread) this makes it is really easy to have a separate set of keys for Dev / Test / Prod / etc. If you encrypt the file, sounds like you are going to either have the same encrypted data and decryption key in all of these environments. You won't necessarily have an easy way to change in one but not the others.
Think about the long game - not just passwords but other configuration information should be loaded and unique per environment (API Keys, etc.)
We use this approach for developers too. I don't want each developer to have their own API key, passwords, etc so they don't have access to systems they don't need. I can shutdown a user's API key if a development laptop is lost. I can rotate dev / test / prod API keys and only have to worry about changing in one place, not inform all users that a file has been updated and they need to re-clone.

How can I secure passwords stored inside web.config?

I have added the following settings inside my web.config file to initiate an API call to external system. So I am storing the API URL + username + password as follows:-
<appSettings>
<add key="ApiURL" value="https://...../servlets/AssetServlet" />
<add key="ApiUserName" value="tmsservice" />
<add key="ApiPassword" value="test2test2" />
Then inside my action method I will be referencing these values when building the web client as follows:-
public ActionResult Create(RackJoin rj, FormCollection formValues)
{
XmlDocument doc = new XmlDocument();
using (var client = new WebClient())
{
var query = HttpUtility.ParseQueryString(string.Empty);
foreach (string key in formValues)
{
query[key] = this.Request.Form[key];
}
query["username"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiUserName"];
query["password"] = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiPassword"];
string apiurl = System.Web.Configuration.WebConfigurationManager.AppSettings["ApiURL"];
But in this was I will be exposing the username and password and these can be captured by users, so my question is how I can secure the API username and password?
You can encrypt the web.config with aspnet_regiis. This is to stop people with access to your server from reading sensitive information.
By the way, I would put your config settings inside a class, that can then be injected into your controllers - it will make unit testing easier.
Generally, web.config is a secure file and IIS does not serve it, therefore it will not be exposed to users who are making requests to web server. Web server only serves specific type of files and web.config is surely not one of 'em.
Quite often you save your database connection string in it which includes password. Now imagine an scenario where web.config was not secure. You have created a major security threat to your application.
Therefore, specially as long as your project is not too big, you should not be worrying about it.
Yet, you may have a better approach but creating a project called "Resources" and save all the critical information such as settings, consts, enums and etc in there. That would be a slick and organized approach.
If you are passing the username/password over the wire though (for example in case of a secured API call), you may want to use https to make sure that information that are travelling are encrypted but that has nothing to do with the security of web.config file.
I know this may sound like over engineering, but if you can, you really should use a secret management service, such as AWS Secrets Manager or Azure Key Vault.
By storing your passwords, even in encrypted form, in your web.config or app.config, you creating several problems for yourself:
now your encrypted passwords are going to be committed to your source control, making them accessible to anyone with the read access. To get rid of them properly, you'll need to do a history rewrite (if you are using git) which is a major pain
while you technically can put your passwords in the machine-level config, and outside of the source control, you'll need to update all those files when your password changes
anyone who's got your encrypted password now can try to decrypt it, using either aspnet_regiis.exe tool (if that's what you used to encrypt it), or if you rolled your own security, reverse engineer that using your source code, figuring out what decryption algo & key it is using
whenever you need to change a password, you'll need to make changes to that file & re-deploy the application.
On the other hand, when you use a secret management service, no secrets or decryption key or algorithm is stored in your source code. Retrieving a secret is as simple as this:
For Azure Key Vault:
var keyVaultUrl = "https://<your-key-vault-name>.vault.azure.net/";
var credential = new DefaultAzureCredential();
var client = new SecretClient(vaultUri: new Uri(keyVaultUrl), credential);
KeyVaultSecret secret = client.GetSecret("<your-secret-name>");
Console.WriteLine($"{secret.Name}: {secret.Value}");
For AWS Secrets Manager (skipped some error handling code):
var client = new AmazonSecretsManagerClient(accessKeyId, secretAccessKey,
RegionEndpoint.APSoutheast2);
var request = new GetSecretValueRequest {
SecretId = secretName
};
GetSecretValueResponse response = null;
response = client.GetSecretValueAsync(request).Result;
This approach has lots of advantages over storage of local secrets:
you don't have to mess with storing different values in configs for Prod/Staging/Dev environments -- just read appropriately named secrets (such as '[Dev|Prod|Stag]DBPassword`
only selected few people can have access to the very important secrects (such as, I dunno, say Transfer all $$$ from Deus account to all E-Coin wallets everywhere authorisation code)
if anyone steals your source code (disgruntled employee, accidental leak) non of your passwords have been leaked
changing a password is easy -- you just update it using the could management console and restart the app(s)
I have written a couple of articles, showing how to set up and read secrets with AWS and Azure on my blog, feel free to check it out if you need step-by-step directions and complete source code:
How to use AWS Secrets Manager to store & read passwords in .Net Core apps
How to securely store and retrieve sensitive info in .NET Core apps with Azure Key Vault
The web.config file is just a file on the file system and so you should (mostly) consider its security in the same way as you would any other file. It will not be served by IIS (unless you make a frankly insane config change to IIS - easy to check for, just try and request it with a browser)
It is good practice to secure your website directory (c:/sites/my-site-here or whatever) using folder permissions to just the website app domain user to read the files (and the deployment user if needed) by using normal windows file permissions
So it may not be necessary to encrypt if you have confidence in the security of the web server. However if you are say on shared hosting, or selling the website code, or the source code is public, then it might be wise to encrypt it. it is a little bit hassle some though.
Why use Web.config?
One of the advantages of having data in Web.config as opposed to storing it in Constants, Enums, etc... is that you can change the data for different environments easily.
Protecting data
The best way to secure the data in Web.config is to encrypt them. Instead of encrypting entire sections in the config file as suggested by Joe and user1089766 you can just encrypt the password string and store it in the config.
Retrieving data
You can use a helper function such as the one below to decrypt the keys.
private const readonly string decryptKey = "";
public string GetFromConfig(string key)
{
var encryptedText = System.Web.Configuration.WebConfigurationManager.AppSettings[key];
var plainText = Decrypt(encryptedText, decryptKey);
return plainText;
}
In this way the decryption key will be inside the project common for all environments. But you can change the data in the web.config easily without recompiling your app.
PS: You can change the decryptionKey and the corresponding data with each version to improve security.
I have to encourage you to separate the code from the Keys. Even if you encrypt the file, someone can clone it from your repo and while it is encrypted, you may no longer be able to track the file, or may get access to a key in the future, etc.
Plus as Art indicated (and maybe others in the thread) this makes it is really easy to have a separate set of keys for Dev / Test / Prod / etc. If you encrypt the file, sounds like you are going to either have the same encrypted data and decryption key in all of these environments. You won't necessarily have an easy way to change in one but not the others.
Think about the long game - not just passwords but other configuration information should be loaded and unique per environment (API Keys, etc.)
We use this approach for developers too. I don't want each developer to have their own API key, passwords, etc so they don't have access to systems they don't need. I can shutdown a user's API key if a development laptop is lost. I can rotate dev / test / prod API keys and only have to worry about changing in one place, not inform all users that a file has been updated and they need to re-clone.

ASP.Net - How to Encrypt password before login while using Login Control

I'm using the ASP.Net Login control to login a web application. When the user submits the form, I want to call a function to encrypt the password before ASP.Net sends the information for authentication.
I tried to add a Customer Validator on the password field and if the password is not empty, the Password.Text will be replaced with the encrypted value. However, it seems ASP.Net still sent the original password for authentication.
I also tried adding a onClick event on the login button to encrypt the password but ASP.Net still sent the original password for authentication.
Is there a way to do this? Thank you!
UPDATE:
I'm sorry for not making this clear. What I need is to encrypt the password at Server Side.
I'm not using ASP.Net Membership to encrypt or hash the password while registering a user. The passwordFormat property has been set to "Clear".
What I am doing is:
While a new user registers, I use a customized function to encrypt the password and save it to database.
When a user tries to login, I want to use the same function to encrypt the password entered by the user and let ASP.Net to authenticate the user.
The problem I'm having is I can't find a way to call the encrypt function before ASP.Net initiate the authentication process.
Hope this makes sense. Thank you.
Allen
You were definitely on the right track with adding the OnClick event. If you are trying to do the encryption client-side then you will need to use the OnClientClick event instead (OnClick happens server-side and OnClientClick happens client-side). I initially assumed you were using it to call a client-side javascript function that does the encryption?
[EDIT]
However, if you are doing the encryption server-side, and using a Login control, then you might want to use the OnAuthenticate event:
<asp:Login id="Login1" runat="server" OnAuthenticate="OnAuthenticate">
</asp:Login>
Then do your encryption here:
private void OnAuthenticate(object sender, AuthenticateEventArgs e) {
bool authenticated = false;
String encryptedPassword = Encrypt(Login1.Password);
authenticated = YourAuthenticationMethod(Login1.UserName, encryptedPassword );
e.Authenticated = authenticated;
}
private bool YourAuthenticationMethod(String username, String encryptedPassword) {
//test the encrypted password against that retrieved from your database using the username
}
Why are you trying to encrypt the password client side before sending it to the server? That's really no more secure than sending the server your plain password. The code you write to encrypt this password is viewable by anyone.
On the server you should use something like this:
public static string createPasswordHash(string pwd)
{
return FormsAuthentication.HashPasswordForStoringInConfigFile(pwd, "md5");
}
Sorry if I misunderstood something about the ASP.NET technology, but it should provide server side application. Therefore, You should not be able to use any "c# code" to encrypt transferring password. If You cannot use secured HTTP (if You could, You wouldn't need to encrypt the password, because all communication would be encrypted), JavaScript is "only way". However, if Your project contains client-side application (the one that is really installed (or run) on client's computer), You can use full c# potential of course.
There is a discussion why would You need it at all. As I see no better explanation, I will try to provide my own: The encryption of password transferred via Internet is essential if you expect that someone will listen to the communication in between client's computer and the server. As I understand it, if user clicks on the log-in button at your site, the page he sees the form on is actually downloaded in his computer and the click only causes a transfer of data from client to server. The data is not encrypted at all and any evil Eve can listen to the transfer obtaining client's plain text password.
But You should be aware that even if You send encrypted password, the encryption covers only plain text password problem. If Your server-side application expects password encrypted with a static algorithm and my only goal (as evil Eve) is to successfully log into the system, I don't actually need to know the password itself, its encrypted form will be good enough. It is quite complex problem and it depends on how much security Your connection really needs - if the costs (or Your effort) are relevant to the risk. If You are really serious with as best as possible security, You should go through some security standards.
The point about seeing Your algorithm written in JavaScript is irrelevant as far as it is well implemented (RSA is both open and easily accessible algorithm, though safe enough).

C# Public Key verify Perl Private key and use as AES key ? Possible and/or viable?

i was thinking if it is possible and/or viable for obfuscation and security to do as the follow:
Client start session with Server (which means a valid login and password was sent and accepted)
Server encrypt a random password with it is Private Key that will then be used into a data encryption using Rijndael's method and send both back to the client (The password which is the encrypted random password and the Rijndael's encrypt data which is what we want for the client to work)
Client will receive both, verify the password to see wether it was encrypt with our pair of keys or not if so it will be used to decrypt the data.
From what i see, Rijndael has some restrictions as of the password size, so would this be even possible (considering the output of the encrypted random password) ??
Is there antoher approuch that would be close to what i was thinking or trying to describe here ?
Is this even worthed ?
The reason i wanted something like this is mostly to make it harder for anyone trying to reproduce what our server communicates with the client, aside from that we use Smart Assembly. I would like you guys to focus on the questions above and forget about packing my code etc. Think of this as a client / server communication security messure if possible.
Best regards.
I can address the first part. If the server encrypts a key with their private key, ANYONE with their public key will be able to decrypt it. This leaves a gaping hole open for a man-in-the-middle attack. In other words, if I intercept the same token you do, I now know the same key that you know. This means that I can see all the traffic that is going back and forth.
The crux of security has always been this initial key-exchange problem. You may want to employ an industry-standard approach, like Diffie-Hellman for the actual key exchange. Hope that helps

Categories