Microsoft Information Protection SDK - Cache storage
The MIP SDK implements a SQLite3 database for maintaining SDK cache storage. Prior to version 1.3 of the Microsoft Information Protection SDK, only two types of cache state storage were supported: On disk and in memory. Both of these types stored certain data, specifically licenses for protected content and policy information, in plaintext.
To improve the security posture of the SDK, we added support for a second type of on disk cache that uses platform-specific cryptographic APIs to protect the database and its contents.
The application defines the cache type when loading the profile as part of the FileProfileSettings
, PolicyProfileSettings
, or ProtectionProfileSettings
objects. The cache type is static for the life of the profile. Changing to a different type of cache storage type requires destroying the existing profile and creating a new one.
Cache Storage Types
Starting in MIP SDK release 1.3, the following storage cache types are available.
Type | Purpose |
---|---|
InMemory | Maintains the storage cache in memory in the application. |
OnDisk | Stores the database on disk in the directory provided in the settings object. The database is stored in plaintext. |
OnDiskEncrypted | Stores the database on disk in the directory provided in the settings object. The database is encrypted using OS-specific APIs. |
Each engine generated by the application generates a new encryption key.
Cache storage is set via one of the profile settings objects, through the mip::CacheStorageType
enum.
FileProfile::Settings profileSettings(mMipContext,
mip::CacheStorageType::OnDiskEncrypted, // Define the storage type to use.
mAuthDelegate,
std::make_shared<sample::consent::ConsentDelegateImpl>(),
std::make_shared<FileProfileObserver>());
When to use each type
Cache storage is important for maintaining offline access to previously decrypted information, and ensuring performance for decryption operations when data has been previously consumed.
- In Memory Storage: Use this storage type for long-lived processes where persisting the policy or license cache information across service restarts is not required.
- On Disk: Use this storage type for applications where processes may frequently stop and start, but must maintain policy, license, and service discovery cache across restarts. This storage cache type is plaintext, so is better suited for server workloads where users won't have access to the state storage. Examples of this would be a Windows service or Linux daemon running on a server, or a SaaS application where only service admins would have access to the state data.
- On Disk and Encrypted: Use this storage type for applications where processes may frequently stop and start, but must maintain policy, license, and service discovery cache across restarts. This storage cache is encrypted, so is better suited for workstation applications where a user may browse and discover the state database. The encryption helps to ensure that prying users won't have access to via the policy contents or protection license content in plain text. It's important to note that in all cases the data is encrypted with keys that the user may be able to access. A skilled adversary is able to decrypt the cache with minimal effort, but this does prevent tampering and browsing.
Supported Platforms for Encryption
Platform | Version | Notes |
---|---|---|
Microsoft Windows | Windows 8 and newer | Windows 7 supports only CacheStorageType::OnDisk |
macOS | High Sierra and later | |
Ubuntu Linux | 16.04 and later | Requires SecretService and LinuxEncryptedCache feature flag. |
Android | Android 7.0 or later | |
iOS | All supported versions |
While the MIP SDK does support other Linux distributions, we didn't test the cache encryption on RedHat Enterprise Linux, CentOS, or Debian.
Note
The feature flag to enable cache storage on Linux is set via mip::MipConfiguration::SetFeatureSettings()
Cache storage database tables
The MIP SDK maintains two databases for cache. One is for the Protection SDKs, and maintaining protection state details. The other is for the Policy SDKs and maintaining policy details and service information. Both are stored in the path defined in the settings object, under mip\mip.policies.sqlite3 and mip\mip.protection.sqlite3.
Note
The MIP SDK does not guarantee compatibility across different versions of its cache. It is advisable to clear all files within the mip\ directory, or any alternative directory changed from default setting, prior to upgrading the application to a new version of the MIP SDK.
Protection Database
Table | Purpose | Encrypted |
---|---|---|
AuthInfoStore | Stores authentication challenge details. | No |
ConsentStore | Stores consent results for each engine. | No |
DnsInfoStore | Stores DNS lookup results for Protection operations | No |
EngineStore | Stores engine details, associated user, and custom client data | No |
KeyStore | Stores symmetric encryption keys for each engine. | Yes |
LicenseStore | Stores use license information for previously decrypted data. | Yes |
SdInfoStore | Stores service discovery results. | No |
Note
The LicenseStore cache requires an identity to be set on the protection engine or file engine.
Policy Database
Table | Purpose | Encrypted |
---|---|---|
KeyStore | Stores symmetric encryption keys for each engine. | Yes |
Policies | Stores label policy information for each user. | Yes |
PoliciesUrl | Stores backend policy service URL for specific user. | No |
Sensitivity | Stores classification rules for a specific user policy. | Yes |
SensitivityUrls | Stores backend sensitivity policy service URL for specific user. | No |
Database size considerations
The database size depends on two factors: The quantity of engines being added to the cache and the quantity of protection licenses that have been cached. As of MIP SDK 1.3, there is no mechanism to clean up the license cache as they expire. There will have to be an external process to remove the cache if it grows larger than is desired.
The most significant contributor to database growth will be the protection license cache. If licensing caching isn't required, either because the service round trips won't impact your application performance, or the cache may grow too large, the license cache can be disable. This is accomplished by setting CanCacheLicenses
on the FileProfile::Settings
object to false.
FileProfile::Settings profileSettings(mMipContext,
mip::CacheStorageType::OnDiskEncrypted,
mAuthDelegate,
std::make_shared<sample::consent::ConsentDelegateImpl>(),
std::make_shared<FileProfileObserver>());
profileSettings.SetCanCacheLicenses(false);
Caching Engines
In MIP SDK, an engine is created for each user performing any authenticated operation. Engines provides an interface for all operations that are performed on behalf of an authenticated identity. As discussed in Profiles and Engines concepts, FileEngine, PolicyEngine, or ProtectionEngine each has two states CREATED
and LOADED
. An engine needs to be created and loaded for it to be able to perform SDK operations. If an engine is not in use, the SDK caches the engine and retains it in CREATED
state as long as possible depending on available resources. Each Respective SDK's profile class also provides a method UnloadEngineAsync
to achieve this explicitly.
Each engine has a unique identifier id
that is used in all engine management operations. The client application can provide an id explicitly, or the SDK can generated one, if it's not provided by the application. If a unique identifier is provided using engine settings objects at the time of engine creation, and caching is enabled in API profile as described above, same engines can be used every time the user performs an operation with the SDK. Follow the code snippets for creating a [mip::FileEngine](./concept-profile-engine-file-engine-cpp.md#create-file-engine-settings)
, [mip::PolicyEngine](./concept-profile-engine-policy-engine-cpp.md#implementation-create-policy-engine-settings)
.
Failing to provide an existing engineId will result in extra service round trips to fetch policy and will fetch licenses that may have already been cached for the existing engine. Caching the engine ID allows the SDK offline access to previously decrypted information and general performance improvements.
Next Steps
Next, learn more about Profile and Engine object concepts to understand how to properly set MIP engine IDs to properly utilize MIP SDK caching.