Follow-up on secret values in a Business Central app

Wow… the community proved to be strong! About 10 days ago I asked to find the secret value in an app file and many of you really tried! Thank you so much!

Before I move on, I would like to draw your attention to this idea. Please give it your vote! For more info, see below at the end of this post.

What happened with the contest?

Because I shared the source code, it was not hard to figure out that the navxdata file contained the secret value. What I really wanted to know was if there are ways to get values out of the navxdata file. And it turned out to be possible. Erik Hougaard was the first to crack that nut. It took only 10 minutes for him! Apparently, he knows some details about the navxdata file that are not so widely known in the community. A few other people also found the value by hex editing into the navxdata file. Well done!

Some other people took the source code and manipulated it to get to the secret value. And for sure, that’s a possibility if you can get to the source code at all. Honestly, I only shared the source code to give you an idea about the technique I used, but not to manipulate the code. Having access to the source code reveals the way how the secret is stored and then the fun is over.

So, what’s next?

Well, my first recommendation is to not share source code when an app contains a secret value. So at least, set showMyCode to false in the app.json file.

As one suggested, you could just store the secret value into the code when you set showMyCode to false. And technically, that’s absolutely right. But I don’t think you should store any secrets in code, so I still believe the approach with the navxdata file is a valid one. Well, as long as Microsoft doesn’t give us any other option.

If you want to deploy your app file in an on-prem environment, then you should only use a runtime package. To prove that point, I’ve uploaded a runtime package here. If you are interested, please download it and try to get to the navxdata file that is inside it and then try to get the secret value. As Erik said, it would only be a little bit more difficult, so let’s see if he (or others) can find it again…

Security by obscurity

An extra level of security that I’ve added to this runtime package is obfuscating the secret value. There are several techniques you could apply, like creating multiple values, mixing them together, XOR-ing values, etc. If you want to get some ideas, then you can look into the code at GitHub. I’ve added some code in the table SecretValue to obfuscate the secret value with bitwise XOR comparison. Not with one, but with two random values. Just for the fun of it, and because it doesn’t exist out of the box in AL code.

local procedure XORSecretValues(
    var SecretValue1List: List of [Byte];
    SecretValue2List: List of [Byte];
    SecretValue3List: List of [Byte])
    Index: Integer;
    SecretValueResult: List of [Byte];
    Byte1: Byte;
    Byte2: Byte;
    foreach Byte1 in SecretValue1List do begin
        Index += 1;
        if Index mod 2 <> 0 then
            SecretValue2List.Get(Index, Byte2)
            SecretValue3List.Get(Index, Byte2);
        SecretValueResult.Add(XORBytes(Byte1, Byte2));
    SecretValue1List := SecretValueResult;
local procedure XORBytes(Byte1: Byte; Byte2: Byte) ReturnValue: Byte
    Binary1: Text;
    Binary2: Text;
    Bool1: Boolean;
    Bool2: Boolean;
    i: Integer;
    XORValue: Text;
    Binary1 := ByteToBinary(Byte1);
    Binary2 := ByteToBinary(Byte2);

    for i := 1 to 8 do begin
        Evaluate(Bool1, Binary1[i]);
        Evaluate(Bool2, Binary2[i]);
        XORValue += Format(Bool1 xor Bool2, 0, 2);
    ReturnValue := BinaryToByte(XORValue);

local procedure ByteToBinary(Value: Byte) ReturnValue: Text;
    while Value >= 1 do begin
        ReturnValue := Format(Value MOD 2) + ReturnValue;
        Value := Value DIV 2;
    ReturnValue := ReturnValue.PadLeft(8, '0');

local procedure BinaryToByte(Value: Text) ReturnValue: Byte;
    Multiplier: Integer;
    IntValue: Integer;
    i: Integer;
    Multiplier := 1;
    for i := StrLen(Value) downto 1 do begin
        Evaluate(IntValue, Value.Substring(i, 1));
        ReturnValue += IntValue * Multiplier;
        Multiplier *= 2;

Limiting access to code with access modifiers

Another recommendation I would like to make is to keep create a small app that only contains the secret value handling. Your other apps can then depend on it. Even better would be if that small base app doesn’t just store the secret value but uses it to get access to an Azure Key Vault in which you have the real secrets. In that way, the Azure Key Vault is a resource that belongs to your app and your other apps can just use that app.

But wait… if other apps can depend on it, then the secrets that are stored in an Azure Key Vault could leave your app and that would expose the values to any other app that takes a dependency. Well, not if you take some precautions.

Mark all the codeunits in that app as internal and non-debuggable. That will prevent dependent apps from calling the code. But that includes your apps, right? But there is another option on top of that. In the app.json you can define what other apps can access the internal objects. That opens the possibility to create trusted dependent apps and still prevent other apps from accessing your internal code.

That’s exactly what I’ve done in the source code. You will find a second app on GitHub, called Demo Secret Values Management. And that app is listed as a trusted app in the app.json of the Demo Secret Values app.

"internalsVisibleTo": [
      "appId": "a9e18554-682c-42c0-a3bf-7427ce225bfb",
      "name": "Demo Secret Values Management",
      "publisher": "Arend-Jan Kauffmann"    

This management app contains code to set the secret values from outside and tocall the function that prepares the table so it can be exported to a navxdata file. When you take this approach, you don’t have to ship the management app, it’s only needed during development and the build process. The corresponding PowerShell commands are available in the Scripts folder. This management app also contains pages to inspect the values in Isolated Storage and the table.

Platform feature

I’ve tried really hard to find a way to store a value inside the app in the most secure way. And for a moment I thought I found one. But after some good reading, I’ve reached the conclusion that there is no other way than trying to hide or obfuscate the code and data. With these blog posts and corresponding code, I’ve tried to inspire you. Please feel free to copy it and modify it to your needs.

What we really need is a platform feature to store secrets. The best option I can think of is creating an Azure Key Vault and an application identity that has access to it. The application identity, which consists of a client id and a secret, should not be shipped together with the app file, but rather be specified outside. For example as extra properties when we upload the app to AppSource. Then those values should become available in our app (and our app only) with AL code, for example with NavApp.AzureClientId and NavApp.AzureSecret. Or, even better, with a built-in feature to get values from the Azure Key Vault.

Can we expect such a platform feature in the future? Well, I certainly believe so. I’ve created an idea to add this as a built-in feature in the AL code. Please review it and give it your vote! The more votes it gets, the higher the chance we will get it. I’ve heard rumours that it is on the radar, but we still need to tell Microsoft that we really need such a feature.

Thanks for following this journey, I hope I’ve given you some ideas on how to handle secret values. If you have any other idea or enhancements, please don’t hesitate to share!

Comment List