Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Core Data Protection on Web Farm

I have a ASP.NET Core application that uses cookie authentication and runs on web farm. The data protection keys are stored in DB. My application implements the IXmlRepository and the ASP.NET Core will call the IXmlRepository.GetAllElements to get the key ring. So, application in all nodes are using the same key ring and cookie encrypted in Node1 can be decrypted in Node2. It works fine.

However, data protection key will expire and ASP.NET Core will generate a new key. ASP.NET Core also cache the keys and will refresh each 18-24 hours. So, when the key expires, Node1 may generate a new key, but all other nodes may not refresh right away and get the new key. Cookie encrypted by Node1 cannot be decrypted in all other nodes.

How can this situation by handled by ASP.NET Core?

I read this https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/web-farm?view=aspnetcore-2.2, it states

The default configuration isn't generally appropriate for hosting apps in a web farm. An alternative to implementing a shared key ring is to always route user requests to the same node.

Is it the only option to route all requests to the same node after user login using that node?

like image 371
kklo Avatar asked May 24 '19 16:05

kklo


1 Answers

In principle, this is no different than any other shared configuration scenario. You need to do two things:

  1. Persist the key ring to a common filesystem or network location path all the processes/apps can access:

    services.AddDataProtection()
        .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));
    
  2. Ensure that all the process/apps are using the same application name:

    services.AddDataProtection()
        .SetApplicationName("shared app name");
    

The second item is less important for a web farm scenario, since it's all the same app and will have the same app name by default. However, it's always better to be explicit, and then if you do need to share with an entirely different app, you're already set up and ready to go.

In short, you need to add the following code:

services.AddDataProtection()
    .SetApplicationName("shared app name")
    .PersistKeysToFileSystem(new DirectoryInfo(@"\\server\share\directory\"));

To @LGSon's point in the comments below your question, you'll also run into an issue with caching/sessions eventually, as well. Regardless of being the same "app", you should think of the each instance in the web farm as a separate app. Each is a separate process, which means they have their own separate memory allocation. If you use memory caching (which is also the default storage for sessions), then that will be available only to the individual process that created it. Each process will therefore end up with separate cache and separate sessions. To share this information, you need to employ a distributed cache like Redis or SQL Server. See: https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-2.2#establish-distributed-caching-services.

Note: Even though there's a "distributed" memory cache, it's not actually distributed. This is just an implementation of the IDistributedCache that stores in memory. Since it stores in-memory, it's still process bound.

like image 114
Chris Pratt Avatar answered Sep 20 '22 10:09

Chris Pratt