As we turn the business logic in Dynamics 365 Business Central from the System layer into extensions, we’re putting extra focus on isolating sensitive information such as Azure Key Vault secrets, and providing a standard way to store the information. One of the changes in that direction is to deprecate the Service Password table, which will happen in 2019 release wave 2. The Service Password table was important because we used it to develop secure features and safely store sensitive information. Moving online, however, required a new approach, so we developed what we call Isolated Storage, which is an integrated platform feature that we expose through application code. Find more information about Isolated Storage here.
Isolated Storage provides additional capabilities for developing extensions. First, Isolated Storage is related to the context of the extension itself so that secrets for one extension cannot be read by another extension, which means that one extension cannot access the data in another.
Second, Isolated Storage provides user-level control through the DataContext option. Application developers can allow all users to access an extension, only users in a certain company, or only a specific user in a specific company.
Third, Isolated Storage is safe, and sensitive data is always encrypted in online version (in OnPrem version the settings is controlled by user). For on-premises versions, we’ve changed how encryption works with secret management. Previously, secrets stored in the Service Password table were automatically encrypted and decrypted according to whether encryption was turned on or off. Isolated Storage is not tightly connected with encryption settings, however. A secret that was inserted while encryption was turned off will remain unencrypted if encryption is turned on. The only scenario where Isolated Storage will follow changes in encryption settings is when you turn off encryption. In that case, the secret will be decrypted.
The new approach requires attention on how to write code that manipulates sensitive data.
1. Do not write wrapper functions around Isolated Storage functionality that can be exposed to other extension (like ReadFromIsolatedStorage and InsertIntoIsolatedStorage) because the caller function can impersonate itself and manipulate sensitive data using the wrong DataContext.
procedure InsertIntoIsolatedStorage(SecretKey : Text; SecretValue : Text;Datascope@1002 : DataScope) : Boolean;
2. If you write an extension that both on-premises and online versions will use, consider that the encryption might be turned off for the on-premises version, so the code should look like:
if not ENCRYPTIONENABLED then exit(ISOLATEDSTORAGE.SET(COPYSTR(SecretKey,1,200),SecretValue ,Datascope)); exit(ISOLATEDSTORAGE.SETENCRYPTED(SecretKey,SecretValue,Datascope));
3. The function that manipulates the secrets should not return sensitive information by VAR.
procedure InsertIntoIsolatedStorage(var SecretKey : Text; SecretValue : Text;Datascope@1002 : DataScope) : Boolean; begin SecretKey := FORMAT(CreateGuid()); if not ENCRYPTIONENABLED then exit(ISOLATEDSTORAGE.SET(COPYSTR(SecretKey,1,200),SecretValue ,Datascope)); exit(ISOLATEDSTORAGE.SETENCRYPTED(SecretKey,SecretValue,Datascope));