Replies: 1 comment
|
Confirming this from the other side, with what the installed package says: you did not miss an encrypt/decrypt path, but the key for one already ships.
So a deployment that sets a malformed key gets an error in the startup logs today ("Plugin-secret encryption will fail once it ships"), which reads as if encryption were active. It is not. On the read path, Two notes that may help scope this: Prior art on the same trade-off. WordPress plugins in this exact category (email providers with AWS credentials) landed on "database by default, constants for real deployments": WP Offload SES recommends A middle step that costs nothing. Until the encryption lands, a plugin can prefer an environment value over the panel value and tell the operator where the credential came from. We ship an SES transport that does this: the host env wins over the settings field, and the plugin's admin page prints the origin of every value plus a warning when a credential is coming from the database. It keeps the settings form useful for people without deploy access, without quietly turning it into the recommended path. Happy to test a PR when the helper lands, on Cloudflare (D1 + Workers) with a real SES credential. |
Uh oh!
There was an error while loading. Please reload this page.
Summary
Plugin settings declared with
type: "secret"are stored in the database as plaintext. Thesecrettype only masks the field in the admin UI; it does not encrypt the value at rest. I'd like EmDash to support encryptingsecret-typed settings before they're persisted (and decrypting on read), so plugins can accept API keys and other credentials through the admin settings form without those credentials sitting in the database in cleartext.Motivation
Several plugins legitimately need to store credentials that an operator enters in the admin UI — for example email providers (Resend/SES API keys), OAuth client secrets, or webhook signing keys. Today the only "safe" place for those is a Worker/environment secret, which means those values can't be managed from the admin UI and require a redeploy to change.
The auto-generated settings form +
secretfield type is exactly the right UX for this, but because the stored value is plaintext, using it for real credentials means anyone with database read access (a D1/SQLite dump, a leaked backup, an unrelated SQL-injection, a support engineer with DB access) can read every stored secret. For something like a session-signing key this is the difference between "annoying to rotate" and "attacker can forge sessions."Current behavior (as I understand it)
SettingFieldunion includessecret, and the admin renders it as a masked input._plugin_storage/ the options table) as plain strings.secretappears to be a display concern only.If I've misread this and there's already encryption I missed, please point me at it and I'll close this.
Proposal
Encrypt values for fields declared
type: "secret"at rest, transparently:getPluginSetting/getPluginSettings/ the in-pluginctx.kv/settings accessor), decrypt transparently so plugin code sees the plaintext, unchanged.Open questions / things for maintainers to decide:
EMDASH_SECRETS_KEY) used with WebCrypto AES-GCM; on Node, an env var likewise. Falling back to an existing site secret is possible but couples concerns.admin.settingsSchemasecretfields, or more broadly to any KV key a plugin marks sensitive.Alternatives considered
secretfield type trustworthy.Context
I hit this building a Better Auth plugin whose config (verification toggles, canonical URL, Google + auth secrets) I wanted fully manageable from the admin UI, mirroring how
emdash-smtpexposes its provider settings. Everything works, but I've had to document a "secrets are stored plaintext; prefer keeping them as Worker secrets" caveat, and left the credential fields as env-fallback rather than truly admin-managed. Encryptingsecretsettings at rest would let plugins like this (and email-transport plugins) offer real credential management from the admin panel safely.Happy to help with a PR if the maintainers are open to the direction.
Filed with AI assistance (Kiro / Claude Sonnet 4.5).
All reactions