wp-security-secrets
Audits WordPress plugin/theme code for secret-handling and credential issues — hardcoded API keys, DB credentials, or signing secrets in source; weak randomness (rand, mt_rand, uniqid) used for security tokens, password reset links, nonces, or session IDs; password storage with md5/sha1/crypt instead of password_hash; insecure cookie flags (missing Secure, HttpOnly, SameSite) on sensitive cookies; secrets logged via error_log / var_dump in production code paths. Use before plugin release, when reviewing auth/registration/login features, when integrating third-party APIs, or when the user mentions "API key", "token", "session", "password reset".
Best use case
wp-security-secrets is best used when you need a repeatable AI agent workflow instead of a one-off prompt.
Audits WordPress plugin/theme code for secret-handling and credential issues — hardcoded API keys, DB credentials, or signing secrets in source; weak randomness (rand, mt_rand, uniqid) used for security tokens, password reset links, nonces, or session IDs; password storage with md5/sha1/crypt instead of password_hash; insecure cookie flags (missing Secure, HttpOnly, SameSite) on sensitive cookies; secrets logged via error_log / var_dump in production code paths. Use before plugin release, when reviewing auth/registration/login features, when integrating third-party APIs, or when the user mentions "API key", "token", "session", "password reset".
Teams using wp-security-secrets should expect a more consistent output, faster repeated execution, less prompt rewriting.
When to use this skill
- You want a reusable workflow that can be run more than once with consistent structure.
When not to use this skill
- You only need a quick one-off answer and do not need a reusable workflow.
- You cannot install or maintain the underlying files, dependencies, or repository context.
Installation
Claude Code / Cursor / Codex
Manual Installation
- Download SKILL.md from GitHub
- Place it in
.claude/skills/wp-security-secrets/SKILL.mdinside your project - Restart your AI agent — it will auto-discover the skill
How wp-security-secrets Compares
| Feature / Agent | wp-security-secrets | Standard Approach |
|---|---|---|
| Platform Support | Not specified | Limited / Varies |
| Context Awareness | High | Baseline |
| Installation Complexity | Unknown | N/A |
Frequently Asked Questions
What does this skill do?
Audits WordPress plugin/theme code for secret-handling and credential issues — hardcoded API keys, DB credentials, or signing secrets in source; weak randomness (rand, mt_rand, uniqid) used for security tokens, password reset links, nonces, or session IDs; password storage with md5/sha1/crypt instead of password_hash; insecure cookie flags (missing Secure, HttpOnly, SameSite) on sensitive cookies; secrets logged via error_log / var_dump in production code paths. Use before plugin release, when reviewing auth/registration/login features, when integrating third-party APIs, or when the user mentions "API key", "token", "session", "password reset".
Where can I find the source code?
You can find the source code on GitHub using the link provided at the top of the page.
SKILL.md Source
# WordPress secrets and credentials audit
A focused review for the secrets layer — what's stored, how it's
generated, how it's compared, how it leaks. Narrow scope by design;
run alongside `wp-security-audit` and `wp-security-deep`.
## When to use this skill
Trigger when:
- Reviewing auth, registration, password reset, or 2FA flows.
- The plugin integrates a third-party API (Stripe, SendGrid, OpenAI,
GitHub etc.) — look for stored credentials.
- The user asks "is my API key safe", "how should I store this token".
- Before wp.org submission (hardcoded secrets are a guaranteed reject).
- The diff contains: `rand`, `mt_rand`, `uniqid`, `md5`, `sha1`,
`password_hash`, `password_verify`, `setcookie`,
`wp_generate_password`, `random_bytes`, `random_int`, `openssl_random_pseudo_bytes`,
`update_option.*api_key`, `update_option.*secret`,
`define.*KEY`, `define.*SECRET`.
## Audit checks
### 1. Hardcoded credentials in source
```php
// HIGH — anyone with repo read access has prod credentials
const STRIPE_SECRET = 'sk_live_4eC39HqLyjWDarjtT1...';
$api_key = 'AIzaSy...';
define( 'MYPLUGIN_API_TOKEN', 'ghp_xxx...' );
```
**Fix:** load from `wp-config.php` constants (server-deployed, not
in repo), an option (`get_option`) with admin-only write, or an env
var via `getenv()`. Provide a settings page for the user to enter
their own.
Search patterns:
- String literals matching common key prefixes: `sk_`, `pk_live_`,
`AKIA`, `ghp_`, `xoxb-`, `AIza`, `Bearer `, `eyJ` (JWT),
hex strings 32+ chars.
- `define( '...KEY...'`, `define( '...SECRET...'`, `define( '...TOKEN...'`
with a non-empty literal.
- Long base64 strings in source (`[A-Za-z0-9+/]{40,}={0,2}`).
Flag any hit. Even "test" keys count — they get committed to prod.
### 2. Weak randomness for security tokens
```php
// HIGH — predictable
$token = md5( uniqid() );
$reset_key = mt_rand();
$session_id = uniqid( '', true );
```
`rand`, `mt_rand`, `uniqid` are NOT cryptographically secure. They're
seeded predictably and the output can be reconstructed.
**Fix — use one of:**
```php
// WP-native, seeded from random_bytes internally
$token = wp_generate_password( 32, false ); // alnum
$token = wp_generate_password( 64, true, true ); // alnum + special
// PHP-native
$token = bin2hex( random_bytes( 32 ) ); // 64 hex chars
$num = random_int( 100000, 999999 ); // 6-digit OTP
// WP nonce (for CSRF only — not a long-lived token!)
$nonce = wp_create_nonce( 'action' );
```
**Don't confuse:**
- `wp_create_nonce` is a 10-char CSRF token, valid 12-24h. NOT for
password resets, API keys, session IDs.
- `wp_generate_uuid4()` is **NOT cryptographically random** despite the
v4 UUID name. WordPress core implements it with `mt_rand()`
(verified in `wp-includes/functions.php`). Use it for non-security
identifiers (post UIDs, log IDs) only. For security tokens use
`wp_generate_password()`, `random_bytes()`, or `random_int()`.
### 3. Password storage
```php
// HIGH — broken
$hash = md5( $password );
$hash = sha1( $password . SALT );
$hash = crypt( $password ); // weak default algo
```
WordPress provides `wp_hash_password()` / `wp_check_password()` —
use these for WP user accounts. **Since WP 6.8** the default algorithm
is bcrypt (was PHPass / portable hash before). To work around bcrypt's
72-byte input limit, WP first HMAC-SHA384 pre-hashes the password,
base64-encodes the result, then runs `password_hash()` with
`PASSWORD_BCRYPT`, prefixing the output with `$wp` to distinguish it
from vanilla bcrypt. `wp_check_password()` still verifies legacy
PHPass hashes from older sites and rehashes on successful login, so
the migration is transparent — **don't reimplement password storage
for WP users**, always go through these functions.
The algorithm and options are filterable (`wp_hash_password_algorithm`,
`wp_hash_password_options`) for sites that want argon2id or stronger
bcrypt cost — but a plugin should not change site-wide defaults unless
explicitly asked.
For non-WP-user passwords (custom auth, API client secrets):
```php
$hash = password_hash( $password, PASSWORD_DEFAULT );
// later
if ( password_verify( $input, $hash ) ) { /* ok */ }
if ( password_needs_rehash( $hash, PASSWORD_DEFAULT ) ) {
// rehash and update store
}
```
`PASSWORD_DEFAULT` resolves to bcrypt across all currently shipped PHP
versions and may change in a future major PHP release — that's the
point of the constant. Always pair it with `password_needs_rehash()`
on verification to migrate stored hashes when the algorithm changes.
Allocate at least 255 bytes for the stored hash column to leave room
for future algorithm output growth. Never roll your own with `hash()`
+ `salt`.
### 4. Cookie flags
```php
// MEDIUM/HIGH — readable by JS, sent over HTTP, sent cross-site
setcookie( 'session', $token, time() + 3600 );
```
**Fix:**
```php
setcookie( 'myplugin_session', $token, [
'expires' => time() + 3600,
'path' => COOKIEPATH ?: '/',
'domain' => COOKIE_DOMAIN ?: '',
'secure' => is_ssl(),
'httponly' => true,
'samesite' => 'Lax', // or 'Strict' for sensitive flows
] );
```
For WP's own auth, **don't roll your own** — use `wp_set_auth_cookie`
which handles all flags correctly.
Flag: any `setcookie` storing a token, session ID, or user identifier
without `secure` (when `is_ssl()`) and `httponly`.
### 5. Secrets in logs and debug output
```php
// MEDIUM — secrets in error_log, often shipped to log aggregators
error_log( 'API request: ' . print_r( $request, true ) ); // includes auth header
var_dump( $_SERVER ); // HTTP_AUTHORIZATION leaks
WP_CLI::log( "Token: $token" );
```
Flag:
- `error_log`, `var_dump`, `print_r`, `var_export` over variables
named `*token*`, `*key*`, `*secret*`, `*password*`, `*auth*`,
`$_SERVER`, full request bodies, or response objects with auth
headers.
- `WP_DEBUG_LOG` enabled in production code path (not a check, but
warn if `define( 'WP_DEBUG_LOG', true )` appears in plugin code —
shouldn't be set by a plugin).
**Fix:** redact before logging:
```php
$safe = $request;
unset( $safe['headers']['Authorization'] );
$safe['body'] = '<redacted>';
error_log( wp_json_encode( $safe ) );
```
### 6. Storing API keys: option vs constant
Both are common — guidance:
- **Constant in `wp-config.php`**: best for site-owner-managed,
rarely-changed secrets. Not in DB, not in repo, only readable by
PHP. Document this in plugin readme.
- **Option (`update_option`)**: needed when the plugin offers a
settings page. Acceptable, but:
- Mark the option `autoload = false` if not needed every request.
- Restrict the setting page to `manage_options`.
- Don't `wp_send_json` or echo the option value back to non-admins.
- **User meta**: only for per-user tokens (e.g. user's own GitHub
PAT). Never for site-wide secrets.
Flag: site-wide secrets stored in user meta, or settings pages
without a capability check.
### 7. Token comparison
Cross-reference with `wp-security-deep` check #8: any secret
comparison must use `hash_equals( $stored, (string) $given )`. Never
`==` or `===`.
## Severity guide
- **HIGH:** hardcoded secret in source, weak randomness for password
reset / session token, password stored with md5/sha1, missing
HttpOnly on session cookie.
- **MEDIUM:** secrets logged, cookie missing SameSite, weak randomness
for non-secret IDs that gain meaning later.
- **LOW:** API key in option without `autoload = false`, no rotation
story documented.
## Report format
Same as `wp-security-audit`. If hardcoded secrets are found, the
report MUST include a top-line warning recommending:
1. Revoke the leaked credential at the issuing service immediately.
2. Rotate to a new secret.
3. Rewrite git history to remove the secret (`git filter-repo` or
BFG) — or accept that the secret is permanently compromised in
the repo.
4. Move to `wp-config.php` constant or settings option.
State this even when the user only asked for a code review — leaked
secrets need real-world action, not just a code change.
## Cross-references
- **`wp-security-audit`**: basic sanitize/escape/nonce/capability.
- **`wp-security-deep`**: object injection, SSRF, CSRF on GET, mass
assignment, file include, mail/zip injection, type juggling, race.
## What this skill does NOT cover
- Cryptographic protocol design (key derivation, signing schemes,
envelope encryption).
- Secret scanning of git history (use `gitleaks`, `trufflehog`).
- Secret rotation processes / KMS integrations.
- Hardware token / 2FA flow correctness beyond storage.
## References
- PHP password hashing:
https://www.php.net/manual/en/function.password-hash.php
- `random_bytes`:
https://www.php.net/manual/en/function.random-bytes.php
- `wp_generate_password`:
https://developer.wordpress.org/reference/functions/wp_generate_password/
- OWASP secret management:
https://cheatsheetseries.owasp.org/cheatsheets/Secrets_Management_Cheat_Sheet.htmlRelated Skills
wp-security-deep
Deep security audit for WordPress plugin/theme PHP code, covering issues beyond the basic sanitize/escape/nonce checklist — PHP object injection (unserialize), SSRF in remote requests, CSRF on state-changing GET handlers, mass assignment via $_POST loops, insecure file include / template injection, mail header injection, ZipSlip in archive extraction, type-juggling in auth comparisons, and TOCTOU race patterns in option/meta locks. Use after or alongside wp-security-audit when reviewing complex plugins, REST APIs, integrations that fetch remote URLs, file processors, or any code that handles uploads, archives, or self-rolled auth tokens.
wp-security-audit
Audits WordPress plugin or theme PHP code for the most common security mistakes — missing nonce checks, capability checks, input sanitization, output escaping, unslashing, SQL preparation, AJAX nopriv exposure, file/path traversal, and unsafe redirects. Use when reviewing pull requests, before releasing a plugin, when the user asks "is this secure", or when handling code that touches $_GET / $_POST / $_REQUEST / $_COOKIE / $_FILES / $_SERVER, admin-ajax / admin-post, REST endpoints, options, user meta, custom DB queries, or file uploads.
webapp-testing
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.
web-artifacts-builder
Suite of tools for creating elaborate, multi-component claude.ai HTML artifacts using modern frontend web technologies (React, Tailwind CSS, shadcn/ui). Use for complex artifacts requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX artifacts.
valyu
Search the live web and 36+ specialised data sources including SEC filings, PubMed, ChEMBL, clinical trials, FRED economic indicators, and patent databases. Use when current, authoritative, or paywalled data is required.
ui-ux-pro-max
AI-powered design intelligence with 67 UI styles, 161 color palettes, 57 font pairings, 99 UX guidelines, and 25 chart types across 15+ tech stacks. Generates complete design systems for any product type with industry-specific reasoning rules.
theme-factory
Toolkit for styling artifacts with a theme. These artifacts can be slides, docs, reportings, HTML landing pages, etc. There are 10 pre-set themes with colors/fonts that you can apply to any artifact that has been creating, or can generate a new theme on-the-fly.
slack-gif-creator
Knowledge and utilities for creating animated GIFs optimized for Slack. Provides constraints, validation tools, and animation concepts. Use when users request animated GIFs for Slack like "make me a GIF of X doing Y for Slack."
skill-creator
Create new skills, modify and improve existing skills, and measure skill performance. Use when users want to create a skill from scratch, update or optimize an existing skill, run evals to test a skill, benchmark skill performance with variance analysis, or optimize a skill's description for better triggering accuracy.
shannon
Autonomous AI security pen testing. Executes real exploits against web applications to find SQL injection, XSS, SSRF, authentication flaws, and IDOR vulnerabilities. Reports only confirmed, reproducible findings — no false positives.
remotion
Create programmatic videos using React and Remotion. Translate natural language descriptions into working Remotion components for product demos, release announcements, explainer videos, and animated content.
planetscale
Design schemas and write queries for PlanetScale serverless MySQL using branch-based workflows. Ensures index coverage, avoids foreign key anti-patterns, and treats every schema change as a reviewable, reversible deploy request.