Saturday, November 18, 2023

Use Obfuscar for obfuscating ASP.Net Core web apps

Obfuscar is free and open source code obfuscation library for .NET. However if you want to use it in web app (ASP.Net Core) it is not that straightforward. First of all you should be ready that Obfuscar may break many things in runtime quite easily. I.e. project will be still compiled successfully but when you will try to run it may get unclear errors. Second - in web apps it is even more tricky to use Obfuscar comparing e.g. with console apps or dll class libraries. In this post I will describe some findings which may help you for using Obfuscar in ASP.Net Core web app.

For using Obfuscar you need to have xml config file. Here is obfuscar.xml file which can be used for obfuscating ASP.Net Core web app (below I will describe it in more details):

<?xml version='1.0'?>
<Obfuscator>
  <Var name="InPath" value="..." />
  <Var name="OutPath" value="..." />
  
  <!-- don't obfuscate Public classes and methods to avoid runtime exceptions -->
  <Var name="KeepPublicApi" value="true" />
  <Var name="HidePrivateApi" value="true" />

  <!-- Additional search path for ASP.Net Core dlls (to avoid) depending on currently used .NET version -->
  <AssemblySearchPath path="C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\6.0.25\ref\net6.0" />

  <Module file="$(InPath)\MyWebApp.dll">
    <!-- don't obfuscate anonymous types to have less problems when pass objects from server side to client side (e.g. in json format) -->
    <SkipType name="*AnonymousType*" skipProperties="true" skipMethods="true" skipFields="true" skipEvents="true" skipStringHiding="true" />
    <!-- don't obfuscate classes generated from cshtml to avoid runtime exceptions -->
    <SkipNamespace name="AspNetCoreGeneratedDocument" />
    <!-- don't obfuscate middlewares to avoid runtime exceptions -->
    <SkipNamespace name="MyWebApp.Middleware" />
  </Module>
</Obfuscator>

First of all we need to use default Obfuscar behavior when public classes and methods are not changed and only private (and internal) classes/methods are obfuscated:

  <!-- don't obfuscate public classes and methods to avoid runtime exceptions -->
  <Var name="KeepPublicApi" value="true" />
  <Var name="HidePrivateApi" value="true" />

Otherwise runtime exceptions will occur.

Second - we need to add additional search path where Obfuscar will search for references ASP.Net Core assemblies:

  <!-- Additional search path for ASP.Net Core dlls (to avoid) depending on currently used .NET version -->
  <AssemblySearchPath path="C:\Program Files\dotnet\packs\Microsoft.AspNetCore.App.Ref\6.0.25\ref\net6.0" />

Without that we will get "Unable to resolve dependency Microsoft.AspNetCore.Mvc.Razor" error during obfuscation. This path depends on actual .NET version which you use in the project. In above example I used .NET 6.0.25:

(I will write separate blog post how to get this .NET version automatically instead of hard coding it in obfuscar.xml)

Third - we need to specify our dll module with few rules:

  <Module file="$(InPath)\MyWebApp.dll">
    <!-- don't obfuscate anonymous types to have less problems -->
    <SkipType name="*AnonymousType*" skipProperties="true" skipMethods="true" skipFields="true" skipEvents="true" skipStringHiding="true" />
    <!-- don't obfuscate classes generated from cshtml to avoid runtime exceptions -->
    <SkipNamespace name="AspNetCoreGeneratedDocument" />
    <!-- don't obfuscate middlewares to avoid runtime exceptions -->
    <SkipNamespace name="MyWebApp.Middleware" />
  </Module>

1. For skipping obfuscation of anonymous types (e.g. if you have controller action which returns JsonResult and returns object of anonymous type from there (which is quite common practice) it will stop working because properties names of returned object will be changed by Obfuscar and since your client side (javascript) scripts will still expect the same properties names in received json object which you specified in C# code they will stop working).

2. For skipping obfuscation of classes from AspNetCoreGeneratedDocument namespace. If you use Razor view engine and cshtml files for views compiler will generate view classes for these cshtml files under the hood. These classes by default have AspNetCoreGeneratedDocument namespace. Obfuscation of these classes also should be skipped because otherwise you will just get empty browser window.

3. For skipping obfuscation of middlewares - otherwise will get runtime exceptions because middleware classes should have predefined public method (async Task Invoke(HttpContext context)). This is needed only if middlewares are defined as internal classes.

With this config basic ASP.Net Core web app should work. Of course I could forgot something or didn't find it yet - in this case will update this post. Also there may be other things which may be needed. If you know them please share in comments.


No comments:

Post a Comment