Treat Service Credentials as an Explicit System Boundary
Credentials become safer to operate when storage, access, rotation, and provider use are explicit parts of one audited boundary.
Flow
A practical credential rotation path
1Plan
Choose provider, operator, source channel, and rollback window.
2Store
Encrypt, fingerprint, and save without echoing the value.
3Verify
Run an explicit non-mutating provider test and record bounded status.
4Switch
Resolve from the new record, observe, and revoke the old value.
To implement a service-credential boundary, begin by writing the threat model in plain language. The database may be copied without the encryption key. A process list may be visible to another operator. A shell history may be retained. Application logs may collect exception text. A developer may run a command in a non-production environment. An administrator may need to rotate a value without being allowed to view it. These are ordinary conditions, not exotic attackers.
Use an authenticated-encryption primitive with a randomly generated nonce for each stored value. Store ciphertext, nonce, authentication tag, key version, and a provider identifier. Keep the encryption key in a separately managed server secret. Add a keyed fingerprint if the product needs to detect whether the replacement is the same value; do not use a plain hash for a credential that might have a small search space. Return a masked hint only if it is designed not to expose a meaningful prefix or suffix.
Create a typed summary function. It should return provider, configured, hint, updated time, last-used time, last-tested time, and test status. It should not return ciphertext, plaintext, encryption-key material, or arbitrary database columns. Make the summary the only object accepted by list and show commands. That design lets tests prove that display paths cannot accidentally serialize a full record.
Make the write command intentionally awkward in the right places. Require a provider identifier. Require a secret-file path or a protected standard-input marker. Reject a secret passed as a positional argument or an unknown flag. Default to a plan that reports provider and character count. Require an authenticated operator and an explicit apply action before insert, replace, or delete. Do not print the value in success output, failure output, debug logs, telemetry, or shell snippets. If the command must be rerun, provide a safe next step that does not include the secret.
Test the command boundary, not only the crypto helper. A useful matrix includes: empty input; a file that cannot be read; stdin input; argv rejection; dry-run output; apply without an operator; first insert; same-value replacement; changed-value replacement; delete preview; delete apply; missing encryption key in production; invalid ciphertext; wrong key version; and provider-resolution failure. Assert that the value is absent from every captured output string. Capture a simulated process argument list and verify it contains no secret.
Define resolution in one function. The function can look for a database record, decrypt it with the configured server key, and return a short-lived value to a provider client. If a bootstrap environment fallback is allowed, make the order explicit and return a non-secret source label for audit. Do not let individual callers read the environment directly. Otherwise a future caller may choose a different precedence and make rotation unpredictable.
Add a provider test operation only when it has a clear purpose. Testing a key should be an explicit operator action, should avoid mutating business data, and should record a bounded status rather than provider error text. “Last tested” should not be confused with “currently valid”: a credential can be revoked after the test. Keep the status timestamp and the test result separate from the secret record’s encryption material.
Audit the authority boundary with a diagram and a code review. The credential resolver may authenticate. It should not decide which order to change, which configuration to publish, or whether a review is complete. A supplemental evaluator can use the resolver while remaining read-only. The command that stores a credential can require an administrator while the command that reads a summary can require a less privileged operator. These are separate permissions and should be represented separately.
Finally, write a rotation runbook. It should say how to stage a new value, verify a safe provider operation, switch consumers, observe failures, revoke the old value, and recover if the new one is rejected. Do not make rotation depend on copying a secret into a ticket. The runbook should reference summary fields, operator identities, and test timestamps instead. A credential boundary is operationally successful when people can maintain it without improvising a new leak.