.Net Core Machine Key alternative for webfarm
Asked Answered
H

1

23

I have been using dotnet core to create an application that runs in a Kubernetes cluster on Linux hosts. As I was testing it noticed getting exceptions when validating the CSRF tokens, that makes sense since I did not edit the machine key to be the same on every instance yet. As i proceeded to set the machine key in web.config i noticed this would no longer work in .Net Core.

As is is now using the DataProtection API, the machine key no longer worked. I tried implementing the api into my application, but when i read i would need to use a network share to exchange the keys between all instances i was stunned. Surely there must be an easier (and better) way to accomplish this without having to rely on a share to be online right?

i tried to set the following in the Startup class in the ConfigureServices method:

services.AddDataProtection().SetApplicationName("DockerTestApplication");

I somehow expected the keys to be generated using the applicationname, but this did not resolve the issue.

I found some interesting docs that all use code that will no longer compile, i guess Microsoft changed up some things:

https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/compatibility/replacing-machinekey

Does anyone know a solution to this problem that will also run on Linux and has the ability to share the tokens over the network between instances?

Thanks in advance!

Highstepper answered 10/4, 2017 at 14:36 Comment(2)
Before you copied machine key from one machine to another, now you can copy those keys the same way? Though note that keys have expiration time (default 90 days) so you should either increase it or create automatic refresh procedure.Rosenstein
Thank you for your answer! I will try to automate that using TeamCity artefacts. I am amazed that the official docs mention using a samba share for exchanging those keys, since that adds a single point of failure and is just generally more hassle than using a static machinekey. I'll try just copying the keys over to the webservers.Highstepper
R
19

I've made some tests to back up my comment about copying keys. First I created simple console application with the following code:

var serviceCollection = new ServiceCollection();
serviceCollection.AddDataProtection()
    .SetApplicationName("my-app")
    .PersistKeysToFileSystem(new DirectoryInfo(@"G:\tmp\so\keys"));
var services = serviceCollection.BuildServiceProvider();
var provider = services.GetService<IDataProtectionProvider>();
var protector = provider.CreateProtector("some_purpose");                
Console.WriteLine(Convert.ToBase64String(protector.Protect(Encoding.UTF8.GetBytes("hello world"))));

So, just create DI container, register data protection there with specific folder for keys, resolve and protect something.

This generated the following key file in target folder:

<?xml version="1.0" encoding="utf-8"?>
<key id="e6cbce11-9afd-43e6-94be-3f6057cb8a87" version="1">
  <creationDate>2017-04-10T15:28:18.0565235Z</creationDate>
  <activationDate>2017-04-10T15:28:18.0144946Z</activationDate>
  <expirationDate>2017-07-09T15:28:18.0144946Z</expirationDate>
  <descriptor deserializerType="Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=1.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
    <descriptor>
      <encryption algorithm="AES_256_CBC" />
      <validation algorithm="HMACSHA256" />
      <masterKey p4:requiresEncryption="true" xmlns:p4="http://schemas.asp.net/2015/03/dataProtection">
        <!-- Warning: the key below is in an unencrypted form. -->
        <value>rVDib1M1BjbCqGctcP+N25zb+Xli9VWX46Y7+9tsoGywGnIg4p9K5QTM+c388i0mC0JBSLaFS2pZBRdR49hsLQ==</value>
      </masterKey>
    </descriptor>
  </descriptor>
</key>

As you see, file is relatively simple. It states creation, activation, expiration dates, algorithms used, reference to deserializer class and of course key itself.

Now I configured asp.net application (so, another application, not that console one) like this:

services.AddDataProtection()
    .SetApplicationName("my-app")
    .PersistKeysToFileSystem(new DirectoryInfo(@"G:\tmp\so\keys-asp"))
    .DisableAutomaticKeyGeneration();

If you now try to run application and do something that requires protection - it will fail, because there no keys and automatic key generation is disabled. However, if I copy keys generated by console app to the target folder - it will happily use them.

So pay attention to the usual security concerns with copying keys, to expiration time of those keys (configurable with SetDefaultKeyLifetime) and using the same version of Microsoft.AspNetCore.DataProtection in all applications you share keys with (because it's version is specified in key xml file) - and you should be fine. It's better to generate your shared keys in one place and in all other places set DisableAutomaticKeyGeneration.

Rosenstein answered 10/4, 2017 at 15:42 Comment(3)
Woah thank you! I will try this tomorrow as soon as I get in office. Automating this would be a much better solution than using the smb share.Highstepper
This works like a charm, thank you very much! For all those who want to attempt this for yourselves, make sure to reference the Microsoft.AspNetCore.DataProtection and Microsoft.AspNetCore.DataProtection.Extensions packages. It will work in both .Net core and regular .Net console applications.Highstepper
@evk, is not it a risk storing the key into a static file, where IIS exposes it to public ( I mean IIS_USR or app pool owner should have explicit access to do file operations on the specified location) ??Lame

© 2022 - 2024 — McMap. All rights reserved.