ASP.NET machinekey set keys in code - c#

I want to adjust the machine keys dynamically in code during runtime, for an IIS hosted ASP.NET MVC 4 website.
The machine keys, encryption and validation keys as well as algorithms to use, are stored in a database. Instead of reading the values from the web.config file, I want to inject those values during application startup and let the system use those instead.
Is there any way to accomplish that without having to change web.config at all (to only change the in memory configuration)?
I have tried accessing the configuration section but it is marked as readonly and is also sealed, so I cannot override IsReadOnly(). However, there is a setter that is an indicator, to me, that there may be a way to potentially remove the readonly flag.
var configSection = (MachineKeySection)ConfigurationManager.GetSection("system.web/machineKey");
if (!configSection.IsReadOnly())
{
configSection.ValidationKey = _platformInfo.MachineKey.ValidationKey;
configSection.DecryptionKey = _platformInfo.MachineKey.EncryptionKey;
...
}
Is there any way to accomplish this?
The only alternative I can see is to use a custom method like AppHarbor, however I would rather stick with the built in approach if it is possible at all.
In case someone asks why I want to do that, the reason is,
this is for a large number of identical websites running in a webfarm. Hence, having non-auto-generated keys is a must (must be the same on each server). Also each website should be isolated and should not share the same keys. As all websites are identical in their physical representation, they share the same physical location. That is the reason the web.config file cannot contain application specific settings.
Edit: It would be very helpful to confirm, at least, if it is simply not possible. As said, one can use custom authentication and encryption methods which would avoid using the machine key settings altogether. Thanks.

It's ugly, but I was able to use reflection to temporarily remove the read-only bit from the config section, set the keys, then restore it:
var getter = typeof(MachineKeySection).GetMethod("GetApplicationConfig", BindingFlags.Static | BindingFlags.NonPublic);
var config = (MachineKeySection)getter.Invoke(null, Array.Empty<object>());
var readOnlyField = typeof(ConfigurationElement).GetField("_bReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
readOnlyField.SetValue(config, false);
config.DecryptionKey = myKeys.EncryptionKey;
config.ValidationKey = myKeys.ValidationKey;
readOnlyField.SetValue(config, true);

There is no way to set this programmatically once the web application starts. However, it is still possible to accomplish your goal.
If each application is running in its own application pool, and if each application pool has its own identity, then check out the CLRConfigFile switch in applicationHost.config. You can use this per-application pool to inject a new level of configuration. See http://weblogs.asp.net/owscott/archive/2011/12/01/setting-an-aspnet-config-file-per-application-pool.aspx for an example of how to use this. You could set an explicit and unique <system.web/machineKey> element in each application pool's custom CLR config file.
This is the same mechanism used by Azure Web Sites, GoDaddy, and other hosters that need to set default explicit machine keys on a per-application basis. Remember to ACL each target .config file appropriately for the application pool which will be accessing it.

Inside running code, probably not.
But, in another application like a Console, I solved using this code:
private static void ChangeWebConfig(string validationKey, string decryptionKey, string webConfigPath)
{
ExeConfigurationFileMap configFileMap = new ExeConfigurationFileMap();
configFileMap.ExeConfigFilename = webConfigPath;
System.Configuration.Configuration config = ConfigurationManager.OpenMappedExeConfiguration(configFileMap, ConfigurationUserLevel.None);
MachineKeySection section = (MachineKeySection)config.GetSection("system.web/machineKey");
section.ValidationKey = validationKey;
section.DecryptionKey = decryptionKey;
config.Save();
}

Related

Is it possible that a NancyHost uses another host name than localhost?

Creating a Nancy self-hosted console application requires the local address including the PORT as parameter:
using (var host = new NancyHost(new Uri("http://localhost:1234")))
{
host.Start();
Console.ReadLine();
}
While customizing the PORT is a valid use case, is it possible to use another HOST than ("http://localhost"). If yes, which ones and for which reason?
Backgroud:
I am creating a custom settings file for the server and I wonder if it is enough to provide a setting 'Port' or is it better to provide a setting 'Host' (or 'URL') that includes the HOST as well as the PORT?
Edit
To avoid hardcoding, the HOST part may be configurable via application settings (App.config) which is different to the custom settings file that is used by the server's administrator. However, I want to keep the custom settings file as simple as possible. Therefere, the question: Is there is any thinkable reason that the part 'http://localhost' should be modified?
The NancyHost constructor needs a valid Uri object, and to create that you can't get around specifying a HOST. Depending on your application make the HOST editable either inside your program, some form of communication or via a settings file. Do not hardcode the HOST as localhost, even if you think it's gonna stay that way, it's good practice to keep things modifiable. If you want your settings file to be as simple as possible, split it into 2 files:
basicSettings
advancedSettings
where advancedSettings only contains things you rarely, if ever, change und basicSettings contain the things you expect to be changed more frequently.
There might be a case at some point in time where you want to connect to another host because NancyHost has moved, either to the cloud or another system in the same network(the latter is more probable). Just in case this happens you should make it modifiable.

write to a different programs app.config c#

I am needing to find away to write to another programs app.config file.
we have a 3rd party application that gets some values from an AppSettings section in its own config. but the only way we can get to change the value is when we call the application and once it runs it does a function which we don't want it to do if it doesn't have the correct values.
We need to encrypt one of the values so what we were think of was creating an application that would save the values in this 3rd party app.config.
saving the values and the encryption aren't a problem its doing it in another applications config file.
Is there a way to set which config file we use or a config path setting?
Regards
Aidan
Thank you for the help with this, I managed to get it done through this code.
string configLocation = string.Format("{0}\\APP.exe.config", Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));
ExeConfigurationFileMap map = new ExeConfigurationFileMap();
map.ExeConfigFilename = configLocation;
Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
txtEndpoint.Text = config.AppSettings.Settings["ENDPOINT"].Value;
This now allows me to write out the set config file values in the appsettings.
Cheers All.
Aidan

Not able to persist new changes in appsetting section in app.config WPF?

Below is the code i am using to update or change the values in appsetting in app.config
Configuration config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
config.AppSettings.Settings["userName"].Value = username;
config.AppSettings.Settings["pwd"].Value = pwd;
config.Save(ConfigurationSaveMode.Modified, true);
ConfigurationManager.RefreshSection("appSettings");
i am using above code to change or update the settings in appsetting section at runtime and want the changes to persist so that when i run the application it should pick the new values from appsettings but here it doesn't happen so the changes made and saved at run time do not persist when i relaunch my application again it has the old default settings. Also i checked app.config in bin/debug but it too had the old values in appsettings. i refered various blogs and post here too as a reference but it got the same code as above but it did not persist the settings.have referred this post
I had the same problem while ago. I would have preferred to put this in a comment but I don't have that privilege. My answer might not be your case but I think is worth to be shared.
May I ask you where your bin folder is located? Windows 7 when you programmatically alter a file that isn't in a user accessible space creates a copy of that file in a Roaming space and there the file will stay. Every time you try to access the file (like your app.config) W7 transparently redirect your readings/writings to this file, so there's a chance that you are modifying the file in the roaming space, leaving the one you are lookin unaltered.
Are the changes you are making still there the successive time you start the application?
Disclaimer/Apology: I'm not an experienced user so if I am saying silly things let me know and I will remove this comment.
See below(from MSDN) and remember app.config is in your project. .exe.config is the actual file name.
Client applications use a global configuration that applies to all users, separate configurations that apply to individual users, and configurations that apply to roaming users. The userLevel parameter determines the location of the configuration file being opened by indicating whether it has no user level (the configuration file is in the same directory as the application) or has a per-user level (the configuration file is in an application settings path determined by the user level).
Specify which configuration to get by passing one of the following values for userLevel:
To get the Configuration object that applies to all users, set userLevel to None.
To get the local Configuration object that applies to the current user, set userLevel to PerUserRoamingAndLocal.
To get the roaming Configuration object that applies to the current user, set userLevel to PerUserRoaming.
NoteNote
To get the Configuration object for a resource, your code must have read permissions on all the configuration files from which it inherits settings. To update a configuration file, your code must additionally have write permissions for both the configuration file and the directory in which it exists.
i got my solution of above problem, my goal was to persist changes done at run time at application or user level. Initially i tried using App.config where i kept default settings for application in appsettings section of app.config, but later after research i got to refer i got to know appsetting does not persist the changes, instead you can use userSettings section where under YourApplication.Property.Settings you can give your userlevel settings and it worked for me. To do this you do not need to go to App.config to do it manually, rather you can do it from the property window of project.
Right Click on your project -> Select Settings Tab on the left-> Now on the right hand side you will see the Resource section , give the ResourceName, Type, Scope and its value and you are done. The same value can be access and change dynamically from Code as well.
Below are Code Excerpt for the same --
Accessing Settings Value
enter code here
userName = Properties.Settings.Default.UserName;
pwd = Properties.Settings.Default.PWD;
Saving New Settings Back
enter code here
Properties.Settings.Default.UserName = userName.ToString();
Properties.Settings.Default.PWD = newPWD..ToString();
Properties.Settings.Default.Save();
And when you will launch your application next time you will get the new changed settings as your default settings.
I hope that helps
Thanks Guys
VJ

App.config - encrypted section error:

I have an application that encrypts a section in the configuration file. In the first time that I try to read the encrypted section from the config file I get an error message: "Unrecognized attribute 'configProtectionProvider'. Note that attribute names are case-sensitive. "
config = ConfigurationManager.OpenExeConfiguration(ConfigurationUserLevel.None);
// Get the section in the file.
ConfigurationSection section = config.GetSection("EncryptedSection");
if (section != null)
{
// Protect the section.
section.SectionInformation.ProtectSection("RsaProtectedConfigurationProvider");
section.SectionInformation.ForceSave = true;
// Save the change.
config.Save(ConfigurationSaveMode.Modified);
}
ConfigurationManager.RefreshSection("EncryptedSection");
Properties.Settings.Default.Reset();
//This is the part where I read the encrypted section:
ConfigurationManager.RefreshSection("EncryptedSection");
System.Collections.IDictionary HSMMasterKeyConfig = (System.Collections.IDictionary)System.Configuration.ConfigurationManager.GetSection("EncryptedSection");
This only happens in the first time that I try to read the encrypted section. I have noticed that the .config file is getting updated immediately after the first save but from some reason I need to restart the application in order to use the encrypted section.
Have you read through this...
http://bytes.com/groups/net/521818-configurationerrorexception-when-reading-protected-config-section
... as it appears to be a conversation involving an MSFT support engineer that directly maps to your situation.
The best way to do this will be to encrypt the app.config sections during installation only. Add an installer class to your project and override the Install method in the class. In this method you should perform the Encryption. You must call base.Install at the end of your overridden Install method. In the Setup Project goto Custom Actions and locate the Install custom action to be pointed with Your Project output [exe or assembly] which contains the definition of your Installer class implementation. This way it will Encrypt your app.Config sections during an installation straight and you will not face this problem. The application will automatically use DPAPI provider to read/write through sections or settings.
For your reference the issue was that the process that was trying to encrypt the config section didn't have admin rights. I added this process to the administrators group and that solved it.
I just ran into the same problem today. Normally, whenever I start an application where the config is in encrypted I always check the config on startup to determine if it's protected. If not the it I follow the standard SectionInformation.ProtectSection method. This is always my first step but today for some reason I decided to reference something from the config before I performed my protection check and got the "Unrecognized attribute 'configProtectionProvider'. Note that attribute names are case-sensitive. " error. All you have to do is run protection code before you reference the config within your normal code and you will no longer have the error.
Try running your Exe in seperate Application Domain. Once your application is loaded in the new AppDomain, check if the Sections are encrypted or not. If not then Encrypt the section and trigger the AppDomain to unload and reload with your executable again.

Why are application settings read-only in app.config?

I have some settings in my app.config which I intend to be 'global' - ie. any user can change them, and all users get the same setting.
But unless I change them to be user settings, they are read only.
Why is this?
And how should I go about persisting my app's global settings?
Edit:
This is actually a windows service application which runs as a service as LocalSystem. It can also be run manually by a local admin with argument "/config", which launches a windows form to edit configuration values.
So it will have write access to %PROGRAMFILES% in both situations.
The way I am accessing my settings is thusly:
Settings.Default.MySetting = MyNewValue;
And when MySetting is set to Application (in my project properties, Settings.settings), I get a compile-time error "MySetting is read only".
I am new to this stuff, and have not yet found a very good explanation of how it is supposed to be done. For example, why do I need to say 'Default', and what does that actually mean? I have no idea. If anyone can point me to an app.config usage tutorial, that would be really helpful.
The real complete answer:
The app.config settings are read-only because there are 2 types of settings:
Application Settings
User Settings
The first won't change unless the application publisher publishes a new version of it. The second is not stored in the app.config, but in a user.config file. In the abscence of this user.config file the app.config provides the default value.
If MySetting is a User Setting:
Settings.Default.MySetting = MyNewValue;
Settings.Default.Save();
It will create a user.config file at [User Local Settings Application Data]\[company name]\[application].exe[hash string]\[version] with the new settings, and those settings will prevail over the settings in the app.config file.
Why: Application settings are intended to be stored in the Application folder under Program Files where the user does not have write privileges.
How: There is no default support for "All Users" but you should be able to setup your own custom config file in a public folder or use a Database.
Simply put: There's no location on a machine that everyone can change, unless you give privileges to do so.
There are several ways to deal with this kind of situation:
You can create a configuration file / some registry settings, put this in the "all users" profile and grant "Everyone" the rights to change that specific file. During installation you can automate the procedure for granting the appropiate privileges and your program can handle the rest.
You can leverage UAC to make sure the current user has the appropiate privileges to change a system-wide setting. This is the recommended approach but also means that not everyone can change specific settings.
You can use a shared database and store your settings in there.
???
I would not recommend to change items in the program files directory or changing the default privileges overthere.
EDIT: As local system you have indeed write privileges to the program files directory. If you get the "Read only" error, it means the settings itself are read only. You'll need to use the configuration manager to be able to change the settings in configuration files.
Hope this helps.
One reason is that the app.config file is in your app's folder under the Program Files directory, and everything in Program Files is read only for standard users by default.
Another is that app.config settings apply system wide. If one user makes a change it will impact other users. Normal users are not supposed to be able to make that kind of change. Anything that can impact multiple users should only be set by a system administrator. Per-user settings belong in each user's Application Data folder.
Not quite sure what you mean here.
Do you mean you allowed users to alter app.config from the UI and the changes are not persisted?
did you call
ConfigurationManager.RefreshSection("appSettings");
and
Configuration.Save();
Configuration Settings are cached in the memory when you starts the application. you can deal with the app.config file as xml to change the values.
I'm using this code (a static method) to change the default settings:
public static bool SetGlobalSetting(string settingName, string settingValue)
{
try
{
XmlDocument xml = new XmlDocument();
xml.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
XmlNode xmlNode = xml.DocumentElement.SelectSingleNode("descendant::setting[#name='" + settingName + "']");
xmlNode.SelectSingleNode("value").InnerText = settingValue;
xml.Save(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile);
Settings s = new Settings();
s.Reload();
return true;
} catch (Exception e)
{
// process the exception as you need
}
return false;
}
}

Categories