This checklist provides a single place to review the things you should consider when protecting your application or library with PreEmptive Protection Dotfuscator. Review this checklist periodically and each time you update to new versions of Dotfuscator.
Development Practices
While not strictly required, use the following development practices:
- A source control system
- A build artifact repository that can store publishable and private build artifacts
- A continuous integration environment separate from developer or integrator systems
Source Files
Treat the following files as source files and maintain them in your source control system:
- The Dotfuscator config file (e.g.
DotfuscatorConfig.xml) - The Renaming Map File (
Map.xml) as an input, only when using Incremental Obfuscation
Include these files as source because they control how protection is applied. This ensures a reproducible build and allows you to track changes to your protection settings. Review changes to these files as you would any other source code.
Build Artifacts
Treat the following files as private build artifacts. You can also gather them in an archive file that is then treated as a private build artifact:
- A log of the textual build output produced when Dotfuscator runs.
- All Dotfuscator-related reports, usuallyloceated in the
DotfuscatorReportsorDotfuscateddirectory.- This includes the renaming map file, typically named
Renaming.xmlorMap.xml. - When using incremental obfuscation, this includes only the output renaming map file. The input renaming map file should be handled as a source file.
- This includes the renaming map file, typically named
These files are essential for maintaining a record of what protections were applied for future reference (auditing, etc.).
The Renaming Map file can decode stack traces from protected applications, but it can also effectively undo Renaming, so it must remain secret. Depending on the sensitivity of your project, you may need to treat this file differently. For example, the build log might be published internally and used to verify protection, but the Renaming Map file might only be accessible to certain personnel because of the possibility of using it to undo protection.
If you preserve unprotected application or library binaries, they must also remain private. All of the files comprising the protected application or library binaries are considered publishable build artifacts.
It is important to assign a distinct version to each build to prevent confusion between binaries, log files, or Renaming Maps. For example, map files can be very different between builds (not using incremental obfuscation), and using the wrong map file to decode a stack trace produces incorrect results.
Build Integration
The continuous integration build should include Dotfuscator protection as a step. In doing so, you should verify the following:
- The build is clean. Any existing binaries or intermediate files are removed before the build to prevent accidentally using results from prior builds.
- The product is built in release mode.
- Dotfuscator ran and declared success.
- The build server and development machines have the latest version of Dotfuscator available installed. Dotfuscator is always improving.
- The build server is using a build license. You must use a Dotfuscator build license when protecting software for general release. Verify this by reviewing the build output.
- You have disabled config file generation.
- Signing was done as expected. The certificate specified in the Dotfuscator configuration must match that used to sign the binaries that the end-user receives.
- The Customer Feedback Program has been left at the default setting of
Enabled, if you and your company are able to do so. When Dotfuscator runs, it may send feature usage and summary project data to our servers through this program, which helps guide our development of new and existing features.
Product Configuration
Adjust your Dotfuscator configuration appropriately for your project. Review Enhance Protection periodically to ensure you are using all the currently recommended enhancements.
Testing a Protected Product
Although you may need to test the unprotected application, it is crucial that the protected application goes through functional testing every time before release. Since your application's code changes over time, doing functional testing ensures that your code changes haven't caused a problem with your protection settings. Although we would expect it to be very rare, it is possible that a bug could cause two subsequent protections of the same app to behave differently.
Periodic performance testing of a protected app is a good idea. This ensures that changes to the app have not started to cause a performance problem that was not present when protection for the app was initially configured. It is a good idea to do performance testing when significant changes are made to a project's configuration.
The following sections describe the major features and discuss considerations with respect to functionality and performance as appropriate to each feature.
Renaming (Functional)
Renaming obfuscation is the most common form of obfuscation and is highly effective, but it can break functionality of an application if a symbol is renamed and a reference to that symbol isn't. In most cases, Dotfuscator can automatically identify name-based references and automatically rename the reference, such as in simple reflection, standard XAML, or manifest files. But more complex references cannot be statically analyzed, and therefore must not be renamed. Dotfuscator attempts to identify such references and automatically exclude appropriate symbols from Renaming, but it cannot do this in all scenarios.
Because of this, it is important to plan time in the project schedule for functional testing after Renaming is applied. It is also important to decide how aggressively to rename symbols. By default, Dotfuscator uses relatively safe settings.
You can configure additional options, such as the alphabet and length of the names used, to improve the strength of Renaming at the expense of increased risk of functional issues.
If your application uses reflection in a relatively straightforward way, it may make sense to also rename reflected classes.
Control Flow (Performance)
In most cases, Control Flow obfuscation is low risk. It is on by default, with the highest settings. In performance-sensitive areas of the code, though, Control Flow obfuscation can degrade performance. Test performance or exclude performance-sensitive areas of the code from Control Flow obfuscation if this is a concern.
Another consideration for Control Flow is that certain obfuscation transforms do not run correctly on Mono (even though they are fine on other .NET runtimes). If this is a concern, you should configure Dotfuscator to use Mono-compatible transforms.
String Encryption (Performance)
String Encryption obfuscation is off by default and you must enable it for particular areas of the code. String Encryption changes how strings are accessed, so it has an effect on performance, especially when strings are retrieved in tight loops.
For heavily GUI-oriented apps that don't have performance-sensitive areas, you can probably enable String Encryption across the entire codebase. For other app scenarios, avoid String Encryption in performance-sensitive areas, but still enable it broadly to avoid drawing attention to sensitive strings.
If you enable String Encryption broadly, test application performance to identify any performance changes.
Checks (Functional)
All Checks in Dotfuscator execute at specific times and places in your application's lifecycle, specifically wherever you configure Dotfuscator to inject them. It is typically appropriate to inject Checks at or near application startup, as well as in other places throughout the app lifecycle, especially around sensitive areas of the code.
All Checks in Dotfuscator share a common set of behaviors you can configured when each Check is triggered. Each of those behaviors requires some forethought:
- Checks can call methods (or set fields) in the application to enable custom behavior when triggered. Custom behavior is used to change application behavior and/or to use third-party analytics platforms. To use custom behavior, you have to modify or add application code. This is only an option if developers are available to change the code.
- Dotfuscator can inject code that automatically performs pre-defined actions (e.g. exiting the app). An important consideration with any Check-triggered behavior (built-in or custom) is the appropriateness of the behavior. For example, if a Debugging Check is triggered, it might be appropriate to exit the app, or to limit the application's feature set. It is probably not appropriate to wipe all the application data, though, as there are potential legitimate scenarios where a debugger could be attached to a production application.
If you use attributes to configure checks, see the Check Attributes page.
Unused Code Removal (Functional)
Dotfuscator can also remove unused code from your application. This helps reduce binary size and reduces the attack surface of the application.
Removal works automatically by identifying the entry points of the application and statically analyzing all the code that is reachable from those points. However, static analysis cannot always find all used code due to factors like dynamic reflection. Because of this, Removal has the potential to break application functionality. Plan time in the project schedule for functional testing after Removal is applied.
Decide how aggressively to remove code. As with Renaming, Library Mode affects Removal, preventing removal of public code elements. Dotfuscator also has additional options that allow for increasing or decreasing the aggressiveness of Removal, though increasing the aggressiveness also increases the risk of functional issues.
Removal is disabled by default; it is important to confirm that the settings are what you want them to be as part of the setup of Dotfuscator.
Linking (Functional)
Linking is a way to combine multiple inputs into a single output. This can simplify deployment scenarios and make it somewhat harder for an attacker to understand the structure of the application.
Linking is disabled by default and you must enable and configure it to be used.
Linking is not typically necessary for applications that undergo further packaging after obfuscation (e.g. Xamarin Android apps are packaged into Android packages such as .apk files).
Watermarking
Watermarking embeds a custom string into an application's structure, such that an attacker can't easily spot it. The watermark can be extracted from the application to identify a particular build.
Configure Watermarking in the PreMark section of the Config Editor. It is disabled by default.
Release Preparation
Follow these steps to prepare for your release:
- Ensure you have followed the steps in the checklist, or that you know and have recorded why a step or feature does not apply.
- Ensure that the protected build has passed functional testing.
- Ensure that reports, map files, and build logs are all archived.
These steps also ensure you are able to diagnose issues in the released product, such as by translating an obfuscated stack trace with the archived renaming map file.