Chapter 26: Security Standards
Chapter 26: Security Standards
Last Updated: 2026-03
26.2 Secret Management by Platform
PHP (WordPress)
// Store: always encrypt API keys before saving to wp_options
update_option('iti_my_plugin_api_key', iti_encrypt($api_key));
// Retrieve: decrypt on use
$api_key = iti_decrypt(get_option('iti_my_plugin_api_key', ''));
Never use define() for API keys. Never put keys in wp-config.php.
Python (Flask / FastAPI)
# .env file (never committed)
ANTHROPIC_API_KEY=sk-ant-...
TAVILY_API_KEY=tvly-...
# In code
import os
from dotenv import load_dotenv
load_dotenv()
api_key = os.environ['ANTHROPIC_API_KEY'] # raises KeyError if missing — intentional
TypeScript (Node.js / Tauri frontend)
// .env file (never committed)
VITE_SOME_PUBLIC_KEY=... // Only non-secret values
// For secrets in Tauri: use IPC to read from Keychain, never store in frontend code
const apiKey = await invoke<string>('get_api_key', { service: 'anthropic' });
Swift (iOS/macOS)
// Store in Keychain
try KeychainService.store(key: "ANTHROPIC_API_KEY", value: apiKey)
// Retrieve from Keychain
let apiKey = try KeychainService.retrieve(key: "ANTHROPIC_API_KEY")
Rust (Tauri backend)
// Read from environment (set via tauri.conf.json or OS env)
let api_key = std::env::var("ANTHROPIC_API_KEY")
.map_err(|_| "ANTHROPIC_API_KEY not set".to_string())?;
26.3 Input Sanitization by Platform
PHP (WordPress)
// Text: strip HTML and extra whitespace
$clean = sanitize_text_field($_POST['field'] ?? '');
// Email: validate and sanitize
$email = sanitize_email($_POST['email'] ?? '');
if (!is_email($email)) {
wp_send_json_error(['message' => 'Invalid email.']);
}
// URL: validate and sanitize
$url = esc_url_raw($_POST['url'] ?? '');
// Integer: ensure positive integer
$count = absint($_POST['count'] ?? 0);
// HTML content: strip disallowed tags and attributes
$html = wp_kses_post($_POST['content'] ?? '');
Warning:
sanitize_text_field()is for plain text. Never use it on HTML content — usewp_kses_post()instead.
Python
from pydantic import BaseModel, Field, validator
class UserRequest(BaseModel):
message: str = Field(..., min_length=1, max_length=10000)
email: str | None = None
@validator('email')
def validate_email(cls, v):
if v is not None:
import re
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', v):
raise ValueError('Invalid email format')
return v
TypeScript
function sanitizeInput(input: unknown): string {
if (typeof input !== 'string') {
throw new Error('Expected string input');
}
return input.trim().slice(0, 10000); // enforce max length
}
26.4 Output Escaping by Platform
PHP (WordPress)
// HTML context: escape for display in HTML
echo esc_html($user_data);
// HTML attribute context
echo '<input value="' . esc_attr($value) . '">';
// URL context
echo '<a href="' . esc_url($url) . '">';
// JavaScript context
echo '<script>var data = ' . wp_json_encode($data) . ';</script>';
Python (Jinja2 templates)
Jinja2 auto-escapes by default when autoescape=True. Verify this is set:
from jinja2 import Environment, select_autoescape
env = Environment(autoescape=select_autoescape(['html', 'xml']))
React/TypeScript
React escapes by default when rendering with {}. Never use dangerouslySetInnerHTML unless the content has been explicitly sanitized with a library like DOMPurify.
26.5 CSRF Protection (WordPress)
Every WordPress AJAX handler must verify a nonce:
// On the frontend, output a nonce for the AJAX action
wp_nonce_field('iti_my_action_nonce', 'nonce');
// Or in JavaScript
const nonce = iti_my_plugin_data.nonce; // passed via wp_localize_script
// On the backend, verify the nonce
check_ajax_referer('iti_my_action_nonce', 'nonce');
// This function wp_die()'s if the nonce is invalid — no further code needed after it
26.6 Database Security
WordPress (wpdb)
// Always use prepare() for parameterized queries
global $wpdb;
$result = $wpdb->get_results(
$wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}my_table WHERE user_id = %d AND status = %s",
$user_id,
$status
)
);
Warning: Never concatenate user input directly into SQL strings.
"SELECT * FROM table WHERE id = " . $_POST['id']is a SQL injection vulnerability.
Python (psycopg2 / SQLAlchemy)
# psycopg2 — use parameterized queries
cursor.execute("SELECT * FROM sessions WHERE user_id = %s", (user_id,))
# SQLAlchemy ORM — parameterized by default
result = session.query(Session).filter(Session.user_id == user_id).all()
Rust (rusqlite)
// Use named parameters
conn.query_row(
"SELECT * FROM sessions WHERE user_id = ?1",
params![user_id],
|row| row.get(0),
)?;
26.7 Git Security
# Ensure .env is in .gitignore
echo ".env" >> .gitignore
echo "*.env" >> .gitignore
# Before committing, verify no secrets are staged
git diff --staged | grep -i "api_key\|password\|secret\|token"
Warning: If a secret is accidentally committed, it is in Git history even after deletion. Immediately rotate the compromised key and purge the history.
26.8 Security Event Logging
Log these events with sufficient context to enable audit and incident response:
| Event | What to Log |
|---|---|
| Failed authentication | Timestamp, user/IP, reason |
| Invalid nonce | Timestamp, action, user/IP |
| Unauthorized access attempt | Timestamp, user, resource, action |
| API key error | Timestamp, service, error code (not the key itself) |
| Unusual data access | Timestamp, user, what was accessed |
// WordPress logging example
error_log(sprintf(
'[ITI Security] Unauthorized access attempt: user=%d, action=%s, ip=%s, time=%s',
get_current_user_id(),
sanitize_text_field($_POST['action'] ?? 'unknown'),
$_SERVER['REMOTE_ADDR'] ?? 'unknown',
current_time('mysql')
));
26.9 Application Security Skills
For deeper security analysis beyond coding standards, five application security skills are available:
| Skill | Specialty |
|---|---|
appsec-api-security-engineer |
API attack surface analysis, rate limiting, abuse prevention |
appsec-cloud-container-security-engineer |
Kubernetes hardening, image scanning, IaC review |
appsec-devsecops-engineer |
CI/CD pipeline security, SAST/DAST, supply chain hardening |
appsec-iam-security-engineer |
OAuth/OIDC hardening, JWT analysis, authorization models |
appsec-security-testing-ir-engineer |
Pen testing, fuzzing, incident response, forensics |
All five share a common core of threat modeling (STRIDE/PASTA), secure code review, and vulnerability assessment with CVSS scoring.
Previous: Chapter 25 — Testing | Next: Chapter 27 — Deployment
