Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET machinekey set keys in code

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.

like image 338
michael81 Avatar asked Aug 26 '13 14:08

michael81


2 Answers

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);
like image 54
Chadwick Avatar answered Oct 16 '22 13:10

Chadwick


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.

like image 20
Levi Avatar answered Oct 16 '22 12:10

Levi