Software & practice

Organise credentials and application keys

Treat inventory, permissions, rotation and recovery as one understandable lifecycle.

Symbolic red key cabinet on charcoal stone holds two ordinary brass keys. German tags identify New Active and Old Verify only. An ivory Revocation envelope and red pencil lie in front.

A secret needs a purpose and an accountable owner

Ordered credential management starts with understanding why each access credential exists. A string called API key explains neither its permissions nor its owner. Service, environment, purpose, accountability and revocation route should be known for every credential. These belong in an understandable inventory while the actual secret remains only in the protected locations intended to hold and supply it.

This article combines that organisational task with an executable rotation example. Two keys generated solely for the experiment demonstrate transition from an old version to a new one. No production credentials are read or replaced. The complete Node module checks real HMAC messages and shows what is accepted during overlap and after the old key has been removed from the current configuration.

A working demonstration key is not permission to regenerate existing original keys. Some keys form long-term identity or decrypt existing information. Replacing them without understanding that role can cause irreversible loss of access. The appropriate transition follows actual use and a verified recovery route rather than a blanket cleanup instruction applied to every string labelled secret.

Distinguish passwords, application keys and public identifiers

A personal password, technical API key and public client identifier have different jobs. A private signing key also has different consequences from a replaceable test credential. An inventory should name these distinctions explicitly. Otherwise a harmless identifier may be unnecessarily treated as secret, while a genuinely powerful technical credential is passed around as though it were ordinary project text.

Personal access benefits from individual accounts, appropriate additional factors and defined recovery. A shared administrator identity makes actions harder to attribute. Technical credentials should similarly belong to a particular process. A background service does not automatically need the rights of someone administering the entire system. Separating identities and purposes makes future review and withdrawal more targeted and less disruptive.

The example concerns a shared technical HMAC key. It checks a small message against alteration and creation without the shared secret. It does not encrypt the content and is not a password store. Any party holding the same secret could create a matching message. The result consequently is not an independent signature proving that one particular individual authored or approved it.

Document the inventory without copying secret values

A useful inventory entry identifies service, purpose, environment, accountable role, permission scope, storage reference, key identifier and last verified transition. It includes revocation and recovery routes. None of this requires placing the actual secret in a general project log. A unique identifier supports operational traceability without turning documentation itself into another disclosure of usable access material.

Before a transition, identify every consumer and the order in which it will change. Include infrequent background tasks and recovery procedures. One successful connection from a workstation does not establish that all consumers have switched. The inventory should expose these actual dependencies and support a concrete completion decision rather than relying on an informal assumption that the main application still seems to work.

Accountability also identifies who handles unexpected failure or suspected exposure. That role needs a known revocation path before an incident begins. The inventory becomes an operational tool instead of merely a collection of opaque strings. A list without purpose or lifecycle offers less help during failure while increasing the number of uncontrolled secret copies that need protection and eventual treatment.

OWASP: Secrets Management

Limit environments and permissions deliberately

Development, testing and production should not accidentally share one powerful credential. Separate technical identities allow appropriate permissions and targeted withdrawal. Verify which separation the platform actually supports. Renaming a configuration entry does not create a separate identity if the same secret with the same authority sits behind both names. Meaningful separation concerns the effective credential and its actual permissions.

Scope should follow the task. A read-only report does not need write permission simply because the interface offers it. Where possible, limit access to the appropriate area and exercise both an allowed and a denied case. A secret’s character sequence does not reliably reveal what the provider permits it to do. Permission review needs the authoritative service configuration and observed behaviour.

The rotation module below implements neither tenant isolation nor role decisions. A valid message code confirms only the displayed cryptographic relationship. It does not decide whether someone may edit a particular customer record. The target system must independently enforce that business authorisation. Even a well-managed application secret should not become a shortcut around every other access decision in the product.

Keep secrets behind the appropriate system boundary

Browser-delivered code cannot conceal an included application secret from its recipients. A shared secret bundled in a mobile application likewise does not establish a general server-side trust boundary. Public identifiers may belong there, but must be distinguished from real secrets. Architecture should keep authority-bearing credentials at the point that genuinely controls the operations they enable rather than relying on an obscure filename or package.

Server environment variables are one possible delivery mechanism. They are not automatically encrypted and do not replace system permissions or appropriate handling of process dumps, diagnostics and backups. A deliberately chosen secret store may provide further lifecycle functions. What matters is its actual integration. A product name alone does not prove that the application retrieves values narrowly and handles them correctly afterwards.

Private configuration files belong outside public delivery with appropriate access controls. Hiding a value in a general document or unusual extension does not create that boundary. Diagnostic helpers should also avoid dumping the entire configuration. Our experiment uses its own short-lived values in process memory and does not include them in saved test outcomes, keeping the evidence separate from usable key material.

Design rotation as a controlled sequence

The small example begins with one active old key. Its initial configuration signs and verifies with that key. Next, a new active key is added while the old one remains available only for verification. Newly created messages already use the new identifier. After controlled overlap, the current verifier no longer contains the old key, so it no longer accepts messages signed with that version.

Overlap does not suit every secret automatically. A compromised credential may require immediate withdrawal. A decryption key may still be needed for historical data. An external provider may prescribe a different transition mechanism. The process must follow the actual credential type. This experiment illustrates bounded HMAC versioning rather than supplying a universal rollout instruction for all credentials, identities and encrypted archives.

Rolling back an application must not accidentally reactivate a deliberately withdrawn credential. Program version and secret configuration therefore need coordinated treatment. Recovery should respect current revocation decisions. This example builds distinct configuration objects and does not silently mutate the original input. Actual production switching and atomic distribution across consumers remain separate integration tasks beyond the local demonstration.

Bind the key identifier to the message

Each demonstration envelope has a key identifier, event identifier, time value and HMAC. The identifier selects an available key and is not itself secret. The signed material also includes a fixed purpose marker and SHA-256 fingerprint of the actual message bytes. These fields and concrete bytes are consequently bound together by the message code rather than only loosely associated through an unsigned label.

Message bytes are not rewritten as JSON before checking. A semantically similar string with different spacing has different bytes and therefore a different code. Sender and receiver need the same agreed representation. This is a deliberately small original teaching format, not a claimed reimplementation of a particular provider’s standard. A real integration must follow that provider’s documented interface and verified signing rules.

Calculation uses Node’s HMAC implementation with SHA-256. Comparison uses equal-length decoded codes and the corresponding Node comparison function. This does not establish that every surrounding operation is automatically timing-independent. The module does not implement its own hash function. Its purpose is to make the use of existing cryptography within this bounded key-version transition understandable and directly executable.

Node.js: Crypto · RFC 2104: HMAC

Execute the complete demonstration module

The following code accepts a bounded set of uniquely named keys. Exactly one entry must be active and match the active identifier. Other available entries may verify only. Each secret must represent exactly 32 bytes as hexadecimal text. That checks form, not randomness. The actual experiment generates values using randomBytes rather than relying on a published fixed secret that readers might accidentally reuse.

The returned object exposes identifiers but does not place secret buffers in ordinary enumerable properties. Normal JSON serialization of that object therefore excludes their values. This is neither memory encryption nor protection against a compromised runtime. It prevents the specific accidental serialization tested here and separates a public configuration overview from the usable secrets captured inside the module’s operations.

import {createHash, createHmac, timingSafeEqual} from 'node:crypto';
const idPattern=/^[a-z][a-z0-9_-]{0,31}$/;
const validTime=value=>Number.isSafeInteger(value)&&value>=0;
const validBody=body=>Buffer.isBuffer(body)&&body.length<=65_536;
const material=(kid,id,issuedAt,body)=>['demo-webhook-v1',kid,id,String(issuedAt),
  createHash('sha256').update(body).digest('hex')].join('\n');
export function makeKeyring({activeId,keys}) {
  if (!idPattern.test(activeId)||!Array.isArray(keys)||keys.length<1||keys.length>4)
    throw Error('Ungültige Schlüsselkonfiguration.');
  const store=new Map();let activeCount=0;
  for(const entry of keys) {
    if (!entry||typeof entry.id!=='string'||!idPattern.test(entry.id)||store.has(entry.id)
      ||!['active','verify_only'].includes(entry.state)
      ||typeof entry.hex!=='string'||!/^[0-9a-f]{64}$/.test(entry.hex))
      throw Error('Ungültiger Schlüsseleintrag.');
    if(entry.state==='active')activeCount++;
    store.set(entry.id,{state:entry.state,key:Buffer.from(entry.hex,'hex')});
  }
  if(activeCount!==1||store.get(activeId)?.state!=='active')
    throw Error('Genau ein aktiver Schlüssel muss zugeordnet sein.');
  return Object.freeze({
    activeId,
    keyIds:Object.freeze([...store.keys()]),
    sign(body,id,issuedAt) {
      if(!validBody(body)||typeof id!=='string'||!idPattern.test(id)||!validTime(issuedAt))
        throw Error('Ungültige Nachricht.');
      return {kid:activeId,id,issuedAt,mac:createHmac('sha256',store.get(activeId).key)
        .update(material(activeId,id,issuedAt,body),'utf8').digest('hex')};
    },
    verify(body,envelope,now) {
      if(!validBody(body)||!envelope||typeof envelope!=='object'||Array.isArray(envelope)
        ||Object.keys(envelope).sort().join(',')!=='id,issuedAt,kid,mac'
        ||typeof envelope.kid!=='string'||!idPattern.test(envelope.kid)
        ||typeof envelope.id!=='string'||!idPattern.test(envelope.id)
        ||!validTime(envelope.issuedAt)||!validTime(now)
        ||Math.abs(now-envelope.issuedAt)>300
        ||typeof envelope.mac!=='string'||!/^[0-9a-f]{64}$/.test(envelope.mac))
        return {ok:false,reason:'invalid_message'};
      const entry=store.get(envelope.kid);
      if(!entry)return {ok:false,reason:'unknown_key'};
      const expected=createHmac('sha256',entry.key)
        .update(material(envelope.kid,envelope.id,envelope.issuedAt,body),'utf8').digest();
      if(!timingSafeEqual(expected,Buffer.from(envelope.mac,'hex')))
        return {ok:false,reason:'signature_mismatch'};
      // Authentication within this demo, not authorisation or replay prevention.
      return {ok:true,kid:envelope.kid,id:envelope.id};
    }
  });
}
export function safeAudit(result) {
  // Deliberately allowlisted, no arbitrary object serialisation.
  if(result?.ok===true)return {event:'verification',outcome:'accepted',kid:result.kid};
  const reason=['invalid_message','unknown_key','signature_mismatch'].includes(result?.reason)
    ?result.reason:'invalid_message';
  return {event:'verification',outcome:'rejected',reason};
}

Bound time and handle replay separately

The verifier accepts a time value within five minutes of the supplied current time. The explicit tolerance applies in both directions. A larger distance is rejected. This can exclude very old or substantially future-dated messages in the experiment, but is not a general guarantee about synchronised clocks or real network duration. Production timing rules need their own requirements and operational review.

The same valid message can still be verified repeatedly inside that window. Tests explicitly demonstrate this. An HMAC and timestamp do not provide durable replay prevention. A writing integration needs an appropriate atomic comparison between the event identifier and processing state. Otherwise a repeated authentic message may trigger the same business action again, even though the cryptographic check behaves exactly as designed.

Keeping these responsibilities separate enables a realistic assessment. The module checks authentication under the shared key and detects the displayed modifications. It processes no business instruction and grants no permission. A production integration additionally needs transport, replay handling, target authorisation and outcome checks. Successful verification is therefore not described as comprehensive security acceptance of an entire endpoint or workflow.

Bound logs through an explicit allowlist

The small audit function copies only intended information: event type, acceptance or rejection and a known identifier or bounded reason. It does not serialize the whole supplied object. An unexpected property containing a secret therefore is not automatically logged. Executed checks exercise that exact boundary with extra unexpected values rather than assuming a generic logger will recognise every possible confidential field.

Allowlisting fields is more understandable here than attempting to remove every conceivable secret from an arbitrary complete dump afterwards. It remains only one component. Other logging paths, HTTP helpers and crash reporting can still include sensitive information. Review those actual paths in the real application. This local evidence concerns only the displayed audit function and the fields it demonstrably emits.

Operators need an understandable rejection reason, not a complete copy of the secret or confidential message content. Exact values should not enter general public output merely to simplify debugging. At the same time, authorised owners should identify the affected credential through its identifier. Useful logging combines that operational connection with an appropriate limit on the content collected and retained.

OWASP: Logging

Actual rotation results: overlap verifies old and new HMAC messages; the new-only configuration rejects the removed key. A repeated valid message still verifies, explicitly exposing missing replay prevention. Explanatory labels are German.
Actual returns from the displayed Node module. New messages use the new key and the old key is absent after local reconfiguration. The 54 checks explicitly include a replay that still succeeds. Explanatory image labels remain German.

Review 54 executed checks across three configurations

The experiment contains 54 executed conditions in Node 24.22.1. Initially only the old key applies. During overlap, old and new messages verify while new messages use the new identifier. In the final configuration, an old message fails because its key is no longer available; the new message remains verifiable. Transition does not silently change the original configuration object passed to the first instance.

Further cases alter content, event identifier, time and key identifier, causing verification to fail. Malformed or short codes, additional envelope properties, oversized bodies and incompatible time types are rejected too. Configuration checks cover repeated identifiers, two active entries, no active entry and incompatible key material. Error messages contain neither of the experiment’s key values, keeping diagnostic evidence free of those secrets.

The illustration presents actual returns from the three configurations and an explicitly successful replay case. It is not a screenshot of a production secret store and displays no live values. Test keys are generated inside the process and excluded from saved evidence. These findings establish the small rotation and its limitations, not recovery capability or security acceptance of an existing customer application.

Treat exposure through authoritative withdrawal

Deleting a usable key from one file does not revoke its authority after exposure. Treatment must involve the responsible service or authoritative application configuration. Appropriate immediate action depends on purpose and consequences. Consumers then need targeted changes and checks. Older copies may still exist in logs or backups, which is why removing one visible occurrence cannot establish that the access itself has ended.

Investigate the cause as well. Did the value leave through diagnostics, a published package or unsuitable sharing? Without that answer, its replacement may follow the same route. The response should cover accountable ownership, withdrawal, verified replacement and corrected output paths. A new filename alone does not amount to completed treatment of a still-effective credential accepted by another system.

This article performs no production revocation. The example merely removes an original test key from a new local verifier configuration. It does not replace an incident plan or make blanket claims about external services. The practical distinction is between a stored copy of secret material and the effective authority the responsible counterpart continues to associate with that material. Both need the appropriate treatment.

Verify recovery and handover in practice

A securely stored key can still be operationally useless if nobody knows the recovery route. Check whether the accountable role can restore the required access through the intended process. This does not require publishing secrets in general documents. It requires a comprehensible procedure, suitable permissions and a controlled check relevant to the actual service rather than confidence based solely on a backup filename.

Historical values are especially important for long-lived signing or decryption identities. Backups need to match the actual data. A newly generated string cannot automatically replace a lost original key. Existing originals therefore should not be deleted or silently regenerated without understanding their purpose. The decision follows the real identity and its dependencies, including the consequences for historical verification or access.

Team handover reviews owners, consumers, identifiers and recovery routes. Personal rights no longer needed should be withdrawn through the appropriate responsible process. Possession of an old project copy must not remain the sole description of an access relationship. A good handover leaves a current understandable inventory rather than another uncontrolled distribution of secret values to an expanding group of recipients.

Maintain order around actual dependencies

New services, background jobs and environments change the credential inventory. Periodic business review asks whether purpose, scope and accountable role still fit. Infrequently used credentials deserve attention because a missing consumer can stay unnoticed during ordinary daily work. Review follows actual dependencies rather than merely counting entries or interpreting the length of a password list as evidence of good management.

The example makes lifecycle states visible: generate only for a defined purpose, actively use, retain limited verification where appropriate and deliberately withdraw. Some key types additionally need archival or recovery arrangements. These differences belong in the contract. A single blanket action replacing all secrets would not provide adequate management of their different identities, operational effects and historical dependencies.

Ordered management combines limited permissions, clear ownership, protected delivery and verifiable lifecycles. The complete code helps explain one small concrete transition. A real application then needs review of its actual credentials and authoritative counterparts. This creates a usable inventory whose order does not depend on multiplying secret copies or making security promises that the evidence cannot establish.

Different credentials require different questions

TypeCore review question
Personal accountIndividual accountability and recovery defined?
Technical API credentialEnvironment, consumer, scope and revocation known?
Shared HMAC keyCounterparts and overlap state coordinated?
Decryption keyHistorical data and original material recoverable?
Private signing identityReplacement consequences for identity understood?
Public identifierGenuinely distributable without secret authority?

Checklist for the real inventory

  • Document purpose, environment, owner and consumers.
  • Keep secret values out of general project logs.
  • Distinguish public identifiers from usable secrets.
  • Limit permissions and environments to actual requirements.
  • Review delivery, diagnostics and backups concretely.
  • Plan rotation for the actual key type and provider.
  • Do not replace historically important originals without review.
  • Test activation, overlap and withdrawal with counterexamples.
  • Handle replay prevention and business authorisation separately.
  • Exercise recovery and handover through intended procedures.

Sources and review scope

Primary sources reviewed on 9 November 2026. Original bounded HMAC teaching format with 54 executed Node conditions. Ephemeral test keys only; no production credential read or changed. No complete endpoint or recovery acceptance claimed.

Let’s move forward

What would you like to change?

An idea, a specific challenge or your next step with AI. We’re listening.

Let’s talk