Cross-platform secure storage for secrets and tokens that can be sync'd

Use case or problem

Currently, it’s not safe to store secrets or credentials in Obsidian in a cross-platform safe way. In terms of integrations with outside services.

On the desktop we can use the Electron or Node.js APIs to keep things partially secure. Using the Electron safeStorage API means I can encrypt and decrypt my data using my own mechanisms and I don’t need to worry about key storage since this is delegated to the OS (KeyChain on Mac, Windows credentials stores and whatever credentials store is installed by the window manager on Linux).

On mobile there is no such API that I’m aware of?

Proposed solution

I would propose that Obsidian API include some mechanisms that can be used cross-platform to allow for the encryption and decryption of data.

My suggestion would be to emulate the electron safeStorage API. This would allow plugin developers to use a safe encryption/decryption scheme that is safe across mobile and desktop.

The key management and sync could then be managed safely by Obsidian in the background.

1 Like

While working with a plugin designed to sync vaults to version control and potentially share them publicly, I noticed some plugins store sensitive data, like API keys, in the .obsidian folder, either as standalone files or inside the plugin’s data.json. It’s possible (if generally unlikely) for users to unwittingly share this information.

If there were a standard location for plugin developers to store secrets and sensitive information, e.g., a .secrets folder, that would allow users (and plugins) to safely share non-sensitive information while being more guarded with sensitive data.

Plugins could set a flag in their manifest declaring that they use the .secrets folder, and a standard notice could be displayed. Other plugins could use the manifest list (along with a standard location) to perform automated audits, encrypt/decrypt secrets files on the fly, etc.

7 Likes

Hello everyone,
I am working on the Google calendar plugin and have encountered the problem of storing the access and refresh API token as well as the custom client ID and secret inside obsidian. I have postponed this issue long enough by storing the data inside the local storage of obsidian. My problem is, that everyone has access to this storage.

I looked into using electrons saveStorage or third party libraries using the system keychain to encrypt the data, but came to the conclusion that this doesn’t add any additional security, plus it is not compatible for mobile users.

My Idea would be to add an option inside the settings to password protect the confidential data using AES. The drawback would be to enter the password every time obsidian starts.

I would like your opinion and insides on this idea. Would you use such an option, if it increases security? Would this add security?

I would like to implement OAuth login for Imgur plugin. And it will need to store tokens somewhere. Plugin settings is obviously not an option, because it stores data in plain text inside of a vault. In Discord chat I was suggested to try LocalStorage, but with a caution that it won’t probably work in mobile version. While Imgur plugin does not target mobile platform, what is the most preferable place to store sensitive data? And if there is no such a place, it’s probably worth thinking about creating such an API for plugins.

Quoting snyk.io:

local storage should never be used for sensitive information such as passwords or personal information

PS One other thing which prevents me from implementing OAuth login is that I can not receive all the data coming to Obsidian protocol handler as part of a callback URL, see the detailed description here

5 Likes

Did you ever find a good workaround?

Nope, I did not try to find anything (now I am stuck with OAuth authentication implementation, I do not even have secrets to store yet). But as soon as I will be able to proceed I would stick with localstorage until there is no better API for secrets. I think for me localstorage is a good fit (Imgur authentication can be shared between multiple vaults, and I do not worry about mobile support since I am not targeting it)

A couple of thoughts I had recently on this topic:

  • I do not think localstorage is a good fit for sensitive data, because it gets shared between any other plugin, and some evil plugin could dump Object.entries(localStorage) and steal your secrets (if they will be stored there).

  • On the other hand I like that localstorage data is available between Vaults. For my Imgur plugin it means that once authenticated in one Vault, you do not need to repeat this step for any other Vault.

Summarizing: localStorage is not an option for really sensitive data. I would like to have some interface for Obsidian which would isolate secret data of different plugins from each other, allowing to share such data for the same plugin between vaults on a single system.

1 Like

Hi there, I’m wondering if anyone found a save place to store sensitive information? I do not have requirements of sharing these across other vaults). However, mobile support is required, the user simply has to enter these information for each vault / device again. Otherwise, I’m tempted to manage the encryption my self but it would be overhead, for a common problem I suppose

Can you store the token somewhere online? There are plenty of free services where you can bring up a simple server of a few lines written in Python or Go which will return that token on demand. After authentication of course. Let’s Encrypt will provide a secure connection.
That way you won’t care about which platform you are using.
I know it is a bit complicated but can’t think about anything else.

Kinda. I’ve partially written a plugin for developers that exposes an API to handle safe secret handling.

I’m using non-exportable keys and relying on the WebCrypto API standard to protect the keys in-flight.

  • Each device gets it’s own non-extractable $TBD_Key_Algo (signing) and $TBD_Key_Ex_ALGO (key exchange) key pairs.
  • Each Vault has a Vault Root Key (VRK) used to wrap per-file DEKs.
  • Each Plugin operates in it’s own key space which is derived from the VRF with a HKDF.

I’m currently a little stuck. The $TBD_Key_Algo and $TBD_Key_Ex_ALGO I want to use are Ed25519 and X25519. I could pivot to ECDSA and and ECDH and that’s what I’m currently looking into, with a progression plan to allow the user to migrate to new key algorithms.

ECDSA and ECDH isn’t the best option… and RSA is… 48 years old and I’m loathe to go anywhere near key generation and crypto functions on mobile devices where the keys are getting progressively longer (recommendation these days is for 3072bit keys)…

The current API looks like this:

const shard = app.plugins.getPlugin("obsidian-shard")?.api;

// 1. Request a scoped capability
const cap = await shard.requestCapability("my.plugin.id", { ops: ["seal","open"], ttlSec: 120 });

// 2. Ensure a vault and local identity exist
const meta = await shard.Vault.ensureVault();

// 3. Encrypt (seal) data
const plaintext = new TextEncoder().encode("Secret data");

const sealed = await shard.Crypto.seal(meta.vaultId, plaintext, undefined, cap, "my.plugin.id", "encryptedfile.bin");

// 4. Decrypt (open) data
const opened = await shard.Crypto.open(meta.vaultId, sealed, undefined, cap, "my.plugin.id", "encryptedfile.bin");

Using per-plugin encryption domains will reduce the blast radius of implementing crypto in an inconsistent way.

Here’s my current TODO list for release:

  • UI for settings and encrypted data management.
  • SPAKE2-P256 VRK recovery implementation - WebCrypto APIs don’t support it.
  • Device authorisation flows - each device will need to be authorised to access the encrypted data.
  • Looooooottttsssss of testing.

The last thing I want to do is push this out, half-finished and others pick it up and encrypt users data irretrievably while I’m fiddling with the API.

~ R