Release Checklist
This checklist provides a single place to review the things to consider when protecting your app or library with PreEmptive Protection Dotfuscator. This checklist should be reviewed from time to time and each time you update to new versions of Dotfuscator.
Development Practices
Although not required, we assume that you have:
- A source control system
- A build artifact repository (capable of storing publishable and private build artifacts)
- A continuous integration environment distinct from developer or integrator systems
Source Files
The following files should be treated as source files and should be maintained in your source control system:
- The Dotfuscator config file (e.g.
DotfuscatorConfig.xml
) - Only when using Incremental Obfuscation: the Renaming Map File as an input (
Map.xml
)
You should include these files as source because they control how protection is applied, and thus are important in having a reproducible build and tracking when changes to the protection settings were made. Review changes to these files as you would for any other source code.
Build Artifacts
The following files should be treated as private build artifacts (or could be gathered in an archive file that would then be treated as a private build artifact):
- A log of the textual build output produced when Dotfuscator runs.
- All Dotfuscator-related reports, usually included in the
DotfuscatorReports
orDotfuscated
directory.- This includes the renaming map file, typically named
Renaming.xml
orMap.xml
. - When using incremental obfuscation, this includes just the output renaming map file. The input renaming map file should be handled as a source file, as noted above.
- This includes the renaming map file, typically named
Some of these files are important for maintaining a record of what protections were applied and should be available for future reference (auditing, etc.). The Renaming Map file can be used to decode stack traces from protected applications. The Renaming Map file can be used to effectively undo Renaming, and thus must remain secret.
Depending on the sensitivity of the project it may be necessary 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 unprotected application or library binaries are preserved, they would need to remain private. All of the files comprising the protected application or library binaries would be publishable build artifacts.
It is important that each build has a distinct version, so that there is no confusion between which binary corresponds to which log file or Renaming Map. 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 will produce 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 ensure that the results of prior builds are not accidentally used.
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. We are always improving our protection.
The build server is using a build license. Only Dotfuscator build licenses may be used when protecting software for general release. This can be verified 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 will receive.
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
It is important that you adjust your Dotfuscator configuration appropriately for your project. See Enhance Protection for detailed instructions. Review this page from time to time to ensure you are using all the currently recommended enhancements.
Testing a Protected Product
Although some testing may need to be done on the unprotected application, it is very important that the protected application goes through functional testing every time before release. Since your application's code will change over time, doing functional testing will ensure 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 has the potential to 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 is impossible to 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.
Additional options, such as the alphabet and length of the names used, can also be configured 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, so if that is a concern then performance should be tested and/or performance-sensitive areas of the code should be excluded from Control Flow obfuscation.
Another consideration for Control Flow is that certain obfuscation transforms will 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 must be enabled 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, String Encryption can probably be enabled across the entire codebase. For other app scenarios, String Encryption should be avoided in performance-sensitive areas, but still enabled broadly to avoid drawing attention to sensitive strings.
If String Encryption is enabled broadly, application performance should be tested to identify any performance changes.
Checks (Functional)
All Checks in Dotfuscator are executed 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 that can be configured when each Check is triggered. Each of those behaviors requires some forethought, as described below.
First, 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, application code will have to be modified and/or added. Therefore, this is only an option if developers are available to change the code.
Second, 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. Static analysis cannot always find all used code, however, because of e.g. dynamic reflection. Because of this, Removal has the potential to break application functionality, so it is important to plan time in the project schedule for functional testing after Removal is applied.
It is also important to 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 must be enabled and configured to be used.
Linking is not typically necessary for applications that will undergo further packaging after obfuscation (e.g. Xamarin Android apps are packaged into Android packages such as .apk files).
Watermarking
Watermarking is a way of embedding 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.
Watermarking can be configured in the PreMark section of the Config Editor and is disabled by default.
Release Preparation
- Ensure the steps in the checklist have been followed, or that the reasons why a step or feature does not apply are known and recorded.
- Ensure that the protected build has passed functional testing.
- Ensure that reports, map files, and build logs are all archived.
These steps will 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.