使用方法
- 复制安装命令,在自己的智能体环境安装前先检查源码包。
- 从原始文档核对所需工具、凭据和支持的宿主。
- 说明输入材料与期望交付物,并对照来源检查输出。
npx skills add https://github.com/wpultimatesecurity/wordpress-security-skills --skill secrets-credentials-management开始前需了解
- 安装不包含模型订阅、API 额度或访问文件的权限。
- 仓库星数和安装量反映活跃情况,不等于独立质量或安全测试。
按源仓库内容呈现,保留标题、案例、代码、表格、链接以及原文引用的演示图片。
Secrets & credentials management
When to use this skill
Use this skill whenever code handles sensitive credentials:
- Storing an API key, webhook secret, or service token from a settings form.
- Building a custom login or token-verification flow.
- Generating reset tokens, API keys, or one-time nonces that must stay secret.
- Logging or exporting data that might accidentally include secrets.
- Deciding whether to use the user's account password or an application-specific
password for API/integration access.
Secrets in source code, reversible password storage, and plaintext API keys in options are common sources of credential leaks.
Related: see the nonces-csrf-protection skill for CSRF tokens and the settings-options-security skill for sanitizing option values.
Core principles (and why they matter)
- Never hardcode secrets. API keys and passwords committed to source control leak to
anyone with repo access and often end up in public repositories.
- Never store user passwords reversibly. WordPress stores passwords as salted hashes
via wp_hash_password(); use wp_check_password() to verify. Do not roll your own hash.
- Prefer wp-config constants or encrypted option storage for service keys. Put keys in
wp-config.php constants or encrypt them at rest with libsodium so the database alone is not enough to recover them.
- Use Application Passwords for API auth. WordPress 5.6+ provides
WP_Application_Passwords
for machine-to-machine auth without exposing the user's account password.
- Never echo or log secrets. Tokens and keys should never appear in HTML, logs, error
messages, shell history, or exported backups.
- Generate tokens with `wp_generate_password()` or `wp_create_nonce()`. They are designed
for cryptographic strength (nonces are short-lived; passwords/tokens can be longer).
Step-by-step implementation
- For user passwords: hash with
wp_hash_password()on save, verify withwp_check_password(). - For service/API keys:
- Accept via a secure settings form (nonce + capability + sanitize).
- Store in a
wp-config.phpconstant if possible, or encrypt withsodium_crypto_secretbox()
using a key derived from wp_salt() (or a dedicated constant) before update_option().
- For machine auth: use
WP_Application_Passwords::create_new_application_password(). - For tokens: generate with
wp_generate_password( $length, false ); store only a hash if you
need to verify it later.
- Sanitize any log/export output to redact known secret keys.
- If a secret leaks, respond in this order:
- Rotate first. Assume compromise the moment the secret left your control — once
pushed to a remote, treat it as captured (clones, forks, and scrapers may already hold it). Issue a new key at the provider, then update the wp-config constant or re-save the encrypted option, and revoke the old key; issuing a replacement alone may leave the leaked key usable. For active abuse, revoke immediately.
- Revoke what rotation cannot cover. Delete leaked Application Passwords
(Users → Profile → Application Passwords) and destroy sessions for affected users with WP_Session_Tokens::get_instance( $affected_user_id )->destroy_all() for each trusted, verified affected user ID. wp_destroy_all_sessions() targets only the current user, not an arbitrary affected user. Session revocation does not revoke Application Passwords. If wp-config.php leaked, rotate all eight keys/salts and exposed database/service credentials. If an encryption key changes, re-encrypt retained secrets before discarding it; salt-derived keys also change when salts rotate. See wp-hardening-best-practices.
- Consider history cleanup after revocation, never instead. If needed, plan a
git filter-repo rewrite with repository owners and collaborators. Do not automatically rewrite history or force-push: publishing rewritten history is destructive and requires explicit approval and coordinated protection of others' work. Old clones still contain the secret and can reintroduce it; arrange re-cloning or careful cleanup. Forks and cached views need separate coordination; consult GitHub Support's removal criteria where applicable.
- Add push protection to block supported secrets when pushing to GitHub, not
when making local commits. Use local secret scanning/pre-commit checks for earlier feedback; neither catches every secret or replaces safe handling.
Supporting references
| Reference | Load when |
|---|---|
| Secrets & credentials management checklist | Before final verification of the secrets & credentials management controls. |
| Secure secret storage | Implementing service API-key storage through wp-config.php constants or encrypted options. |
Common AI mistakes / anti-patterns
Mistake 1 — API key as a string literal
// ❌ Insecure: key is in source control and readable by anyone with file access.
$api_key = 'sk-live-abc123';// ✅ Secure: read from a wp-config constant or an encrypted option.
if ( ! defined( 'MY_PLUGIN_API_KEY' ) ) {
wp_die( esc_html__( 'API key is not configured.', 'my-plugin' ) );
}
$api_key = MY_PLUGIN_API_KEY;Mistake 2 — Storing user passwords in plaintext or with a weak hash
// ❌ Insecure: reversible or weak storage.
update_option( 'my_plugin_user_password', $_POST['password'] );
update_option( 'my_plugin_user_password', md5( $_POST['password'] ) );// ✅ Secure: use WordPress password hashing.
$hash = wp_hash_password( $_POST['password'] );
update_option( 'my_plugin_user_password_hash', $hash );
// Verify later:
if ( wp_check_password( $submitted, $stored_hash ) ) { /* ... */ }Mistake 3 — Logging tokens or API keys
// ❌ Insecure: secret lands in error_log / shell history.
error_log( 'Request failed with token: ' . $api_key );// ✅ Secure: log an identifier or redacted value.
error_log( 'Request failed for token ending in: ' . substr( $api_key, -4 ) );Mistake 4 — Reusing nonces as long-lived secrets
// ❌ Insecure: nonces expire and are not designed for persistent auth.
$token = wp_create_nonce( 'my_api' );
update_user_meta( $user_id, 'my_api_token', $token );// ✅ Secure: generate a dedicated token and hash it for storage.
$token = wp_generate_password( 32, false );
$token_hash = wp_hash_password( $token );
update_user_meta( $user_id, 'my_api_token_hash', $token_hash );
// Hand the plaintext token to the user once, then verify with wp_check_password().Mistake 5 — Storing service keys in plaintext options
// ❌ Insecure: database dump exposes the key.
update_option( 'my_plugin_stripe_key', $_POST['stripe_key'] );// ✅ Secure: encrypt at rest with libsodium (PHP 7.2+).
function my_plugin_encrypt_secret( $plaintext ) {
$key = sodium_base642bin( MY_PLUGIN_ENCRYPTION_KEY, SODIUM_BASE64_VARIANT_ORIGINAL );
$nonce = random_bytes( SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
$cipher = sodium_crypto_secretbox( $plaintext, $nonce, $key );
return base64_encode( $nonce . $cipher );
}
function my_plugin_decrypt_secret( $encoded ) {
$raw = base64_decode( $encoded );
$nonce = substr( $raw, 0, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
$cipher = substr( $raw, SODIUM_CRYPTO_SECRETBOX_NONCEBYTES );
$key = sodium_base642bin( MY_PLUGIN_ENCRYPTION_KEY, SODIUM_BASE64_VARIANT_ORIGINAL );
return sodium_crypto_secretbox_open( $cipher, $nonce, $key );
}Correct code examples
A complete settings-page snippet that stores an API key either as a wp-config constant or encrypted in options is in `references/secure-secret-storage.php`.
Checklist
- [ ] No secrets, API keys, or passwords are hard-coded in source files.
- [ ] User passwords are hashed with
wp_hash_password()and verified withwp_check_password(). - [ ] Service/API keys are stored in
wp-config.phpconstants or encrypted at rest. - [ ] Machine-to-machine auth uses Application Passwords where possible.
- [ ] Tokens are generated with
wp_generate_password()(orwp_create_nonce()for short-lived CSRF). - [ ] Stored tokens are verified against a hash, not compared in plaintext.
- [ ] Secrets are never echoed, logged, exported, or sent to the browser.
- [ ] Leaked credentials are revoked/rotated; affected users' sessions are revoked when warranted.
- [ ] Any history rewrite/publication has explicit approval and coordinated clone/fork cleanup.
- [ ] Settings forms that collect secrets use nonce + capability + HTTPS.



