ASP.NET machinekey set keys in code
Asked Answered
O

3

8

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.

Otes answered 26/8, 2013 at 14:20 Comment(0)
I
5

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.

Intelligence answered 28/8, 2013 at 3:29 Comment(4)
thanks a lot, that helps me (and hopefully others as well). Usually the bigger websites are using separate application pools. But smaller sites are potentially sharing an application pool. But exactly what I need, as I can build around that.Otes
Should the structure of the custom CLR config file be "/configuration/system.web/machineKey"? Is there a way to display or test the actual machineKey value on the application to confirm that the custom CLR config file actually took effect properly?Milkandwater
Yes. You can use ConfigurationManager.GetSection("system.web/machineKey"), cast the returned object to MachineKeySection, and look at the properties hanging off of it to verify that your changes were picked up correctly.Intelligence
Thanks, using GetSection and also the HostingEnvironment I've confirmed that I can modify the maxConcurrentRequestsPerCPU but for some reason the machineKey properties remain unchanged (same values as specified in machine.config). Is there something in particular needed to "enable" the machineKey section? Tried allowDefinition="Everywhere". Each app pool has a unique IUSR identity.Milkandwater
D
9

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);
Dramatist answered 13/3, 2017 at 13:17 Comment(0)
I
5

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.

Intelligence answered 28/8, 2013 at 3:29 Comment(4)
thanks a lot, that helps me (and hopefully others as well). Usually the bigger websites are using separate application pools. But smaller sites are potentially sharing an application pool. But exactly what I need, as I can build around that.Otes
Should the structure of the custom CLR config file be "/configuration/system.web/machineKey"? Is there a way to display or test the actual machineKey value on the application to confirm that the custom CLR config file actually took effect properly?Milkandwater
Yes. You can use ConfigurationManager.GetSection("system.web/machineKey"), cast the returned object to MachineKeySection, and look at the properties hanging off of it to verify that your changes were picked up correctly.Intelligence
Thanks, using GetSection and also the HostingEnvironment I've confirmed that I can modify the maxConcurrentRequestsPerCPU but for some reason the machineKey properties remain unchanged (same values as specified in machine.config). Is there something in particular needed to "enable" the machineKey section? Tried allowDefinition="Everywhere". Each app pool has a unique IUSR identity.Milkandwater
C
1

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();
}
Claypool answered 20/1, 2020 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.