Table of Contents
- OWASP Top 10 (2025)
- HTTP Security Headers
- Transport & Encryption
- Input Validation & Injection Prevention
- Authentication & Session Security
- Password Policy & Hashing
- CSRF, Rate Limiting & Bot Mitigation
- Payment & PCI DSS
- API & Webhook Security
- Supply Chain & Dependency Security
- DNS Security
- Server & Runtime Hardening
- WAF & Firewall Patterns
- Monitoring, Logging & Incident Response
- Privacy & Compliance (GDPR, CCPA)
- Emerging Threats (2025–2026)
- Pre-Launch Security Checklist
- Penetration Testing Protocol
- File Integrity & HTML Monitoring
- Admin Authentication Tiers
1. OWASP Top 10 (2025)
The OWASP Foundation released the eighth edition of its Top 10 in 2025, based on analysis of over 175,000 CVEs and 589 CWEs. Two new categories were added. This is the threat model every site must be built against.
| # | Category | What It Means for Us |
|---|---|---|
| A01 | Broken Access Control | Now includes SSRF. Admin endpoints must verify auth. Client portal must enforce client isolation. No direct object references without ownership check. |
| A02 | Security Misconfiguration | Jumped from #5. Default credentials, debug mode left on, directory listings enabled, missing security headers, verbose error messages. Every tested app had at least one. |
| A03 | Software Supply Chain Failures | NEW. Replaces "Vulnerable Components." npm/CDN dependency attacks, typosquatting, compromised maintainer accounts. See Section 10. |
| A04 | Cryptographic Failures | Weak hashing, plaintext storage, missing HTTPS, insecure random number generation. All passwords must use Argon2id or bcrypt. |
| A05 | Injection | SQL injection, XSS, command injection. All database queries MUST use prepared statements. All output MUST be encoded. See Section 4. |
| A06 | Insecure Design | Flaws in business logic, missing threat modeling, no rate limiting on critical flows. Security must be designed in, not bolted on. |
| A07 | Authentication Failures | Credential stuffing, weak passwords, missing MFA, session fixation. See Sections 5 and 6. |
| A08 | Software or Data Integrity Failures | Unsigned updates, CI/CD pipeline compromise, deserialization attacks. Use SRI on all external scripts. |
| A09 | Security Logging & Alerting Failures | Renamed to emphasize alerting. Without alerts that trigger action, logs are useless. See Section 14. |
| A10 | Mishandling of Exceptional Conditions | NEW. Covers 24 CWEs: improper error handling, failing open instead of closed, logical errors under abnormal input. Errors must fail secure. |
2. HTTP Security Headers
Security headers instruct the browser to enforce protections on behalf of your site. Missing headers leave the browser in its most permissive (least secure) mode.
Required Headers
| Header | Value | Purpose |
|---|---|---|
Strict-Transport-Security |
max-age=63072000; includeSubDomains; preload |
Force HTTPS for 2 years. Prevents SSL stripping attacks. Submit to hstspreload.org for browser preload list. |
Content-Security-Policy |
See nonce-based policy below | Prevents XSS, clickjacking, data injection. Only 21.9% adoption globally — massive competitive advantage. |
X-Content-Type-Options |
nosniff |
Prevents MIME-type sniffing. Stops browsers from interpreting files as a different content type. |
X-Frame-Options |
DENY |
Prevents clickjacking (legacy fallback). Modern browsers use CSP frame-ancestors instead, but keep this for older browsers. |
Referrer-Policy |
strict-origin-when-cross-origin |
Prevents leaking internal paths, query params, and session tokens in the Referer header on cross-origin requests. |
Permissions-Policy |
geolocation=(), camera=(), microphone=(), payment=(), usb=() |
Restricts browser APIs your site doesn't use. Replaces deprecated Feature-Policy. Only 3.7% adoption. |
Cross-Origin-Opener-Policy |
same-origin |
Isolates your browsing context from cross-origin windows. Prevents Spectre-style side-channel attacks. |
Cross-Origin-Resource-Policy |
same-site |
Prevents other sites from loading your resources (images, scripts, fonts) cross-origin. |
Headers to Remove
| Header | Action | Why |
|---|---|---|
Server |
Remove or set to non-informative value | Reveals server software and version (e.g., nginx/1.24). Helps attackers target known vulnerabilities. |
X-Powered-By |
Remove entirely | Reveals backend technology (e.g., PHP/8.3). No legitimate purpose. |
X-XSS-Protection |
Set to 0 or omit |
Browser XSS auditors have been removed from all modern browsers. This header can actually introduce vulnerabilities. |
Expect-CT |
Remove (deprecated) | Certificate Transparency is now enforced by default. Header has no effect. |
Content Security Policy (Strict, Nonce-Based)
The recommended CSP approach for 2025–2026 uses nonces instead of domain whitelists. This prevents XSS even if an attacker finds an injection point.
- Nonce: Generate a cryptographically random value per request. Add
nonce="{RANDOM}"to every<script>tag. Only scripts with a matching nonce execute. strict-dynamic: Allows trusted scripts (with the nonce) to load their own dependencies without whitelisting each domain.frame-ancestors 'none': Modern replacement forX-Frame-Options: DENY. CSP takes priority in modern browsers.object-src 'none': Blocks Flash, Java applets, and other plugin-based content.base-uri 'none': Prevents<base>tag injection that could redirect relative URLs to a malicious domain.
Static Sites (Our Pattern)
For sites that use inline scripts and no server-side nonce generation, use hash-based CSP or a relaxed policy with tight connect-src:
'unsafe-inline' for styles is acceptable. Inline styles are not a meaningful XSS vector because CSS cannot execute JavaScript. The risk is in inline scripts, not inline styles. Avoid 'unsafe-inline' in script-src at all costs.
Nginx Configuration
3. Transport & Encryption
HTTPS Everywhere
All traffic must be served over HTTPS. HTTP requests redirect with 301 Moved Permanently to the HTTPS equivalent. No exceptions.
- TLS 1.3 is the current standard (76% adoption). TLS 1.2 is acceptable as fallback. TLS 1.0 and 1.1 must be disabled.
- HSTS preload: After confirming HTTPS works, submit your domain to
hstspreload.org. This hardcodes HTTPS enforcement into browsers — even the first visit is protected. - Mixed content: A single HTTP resource (image, script, stylesheet) on an HTTPS page triggers browser warnings and breaks trust indicators. Use
upgrade-insecure-requestsin CSP as a safety net.
Certificate Management
- Let's Encrypt with automatic renewal (dpanel handles this)
- Certificates must cover:
domain.com+www.domain.com+mail.domain.com - Monitor certificate expiry — automate renewal at 30 days before expiry
- Never use self-signed certificates in production
Secure Cookies
SameSite Explained
| Value | Behavior | Use When |
|---|---|---|
Strict |
Cookie never sent on cross-site requests | Admin sessions, sensitive operations |
Lax |
Cookie sent on top-level navigations only (clicking a link) | Default for most auth cookies. Best balance of security and usability. |
None; Secure |
Cookie sent on all cross-site requests | Only for true cross-origin needs (third-party embeds). Requires Secure flag. |
Cookie Prefixes
Use cookie prefixes for additional browser-enforced security:
__Host-(strictest): Must be Secure, must not specify a Domain, Path must be/. Prevents subdomain attacks.__Secure-: Must be Secure. Less restrictive but still useful.
api.example.com (same parent domain as example.com) allows SameSite=Lax cookies. Cross-origin APIs require SameSite=None, which is less secure.
4. Input Validation & Injection Prevention
Every piece of data that enters your application from outside is hostile until proven otherwise. Validate at system boundaries. Sanitize before output.
The Four-Stage Pipeline
Every form submission, API request, and webhook passes through these stages in order:
SQL Injection Prevention
SQL injection is OWASP A05 and has been a top web vulnerability for over 20 years. The fix is simple and absolute: never concatenate user input into SQL strings.
XSS Prevention
Cross-site scripting (OWASP A05) injects malicious JavaScript into your pages. Two types:
- Reflected XSS: Malicious input echoed back in the response. Prevented by output encoding.
- Stored XSS: Malicious input saved to database, then rendered to other users. Worse than reflected because it affects every visitor. Prevented by encoding on output and sanitizing on input.
Input Validation Reference
| Field Type | Validation | Sanitization |
|---|---|---|
| Name | Required, 2–100 characters | htmlspecialchars(), trim() |
Required, filter_var(FILTER_VALIDATE_EMAIL) |
filter_var(FILTER_SANITIZE_EMAIL) |
|
| Phone | Optional, 7–20 characters, digits/spaces/dashes/parens | Strip non-numeric for storage |
| Message / Textarea | Required, 10–5000 characters | htmlspecialchars(), trim(), preserve newlines |
| Select / Radio | Value must be in allowed list (whitelist check) | Cast to expected type |
| File Upload | Whitelist extensions, check MIME type server-side, max size | Rename with random filename, store outside webroot |
required and pattern attributes improve UX but are trivially bypassed with curl or browser dev tools. Every validation check must be duplicated server-side. Client-side is for convenience; server-side is for security.
5. Authentication & Session Security
Session-Based Auth (Our Pattern)
All our sites use PHP session-based authentication with httpOnly cookies. The client never stores credentials or tokens — the browser handles the session cookie automatically.
| File | Role |
|---|---|
api/auth.php | Login (POST), register (POST), logout (POST). Returns session cookie. |
api/auth-session.php | Session validation (GET). Returns user data if authenticated, 401 if not. |
js/auth.js | Client-side auth state. Checks session on page load, manages login/logout UI. |
Session Flow
Session Configuration (PHP)
Multi-Factor Authentication (MFA)
MFA adds a second verification layer. The hierarchy of MFA methods, from strongest to weakest:
| Method | Security Level | Status |
|---|---|---|
| Passkeys / WebAuthn / FIDO2 | Highest — phishing-resistant | Recommended standard. Native in all major OSes and browsers. |
| Hardware security keys (YubiKey) | Highest — phishing-resistant | Best for shared devices and high-security admin accounts. |
| TOTP (authenticator apps) | Medium — phishable | Acceptable fallback. Google Authenticator, Authy, etc. |
| SMS OTP | Low — SIM-swap, SS7 attacks | Actively deprecated. Philippines, US (USPTO, FINRA), UAE banning by 2026. |
| Email OTP | Low — account takeover risk | Actively deprecated. Same attack surface as password reset. |
Recovery Codes
- Generate 8–12 single-use codes at MFA enrollment
- Store them hashed (not plaintext) in the database
- Allow re-generation which invalidates all old codes
- Display to user only once at generation time
6. Password Policy & Hashing
NIST SP 800-63B Revision 4 (final July 2025) overhauled password guidance. The old complexity rules are gone. Length and breach checking are what matter.
NIST 800-63B Rev 4 Requirements
| Old Guidance | New Guidance (2025) |
|---|---|
| 8-char minimum with complexity rules | 8-char minimum, 15+ recommended, up to 64 allowed |
| Uppercase + number + special required | No composition rules. Length over complexity. |
| Forced 90-day rotation | No mandatory rotation unless compromise suspected |
| Security questions allowed | Security questions banned. They are not secrets. |
| Optional breach checking | Mandatory breach checking against known compromised databases (e.g., HaveIBeenPwned API) |
| Any hashing algorithm | Memory-hard functions required (Argon2id, bcrypt) |
Hashing Algorithms
Argon2id — Gold Standard (New Projects)
bcrypt — Still Acceptable (Existing Projects)
password_hash() and password_verify() — always. These PHP functions handle salt generation, algorithm selection, and future-proof upgrades automatically. Never use md5(), sha1(), sha256(), or any raw hash function for passwords.
Login Security Rules
- Generic error messages: Always say "Invalid email or password" — never reveal which field was wrong
- Rate limiting: 5 attempts per IP per 60 seconds (see Section 7)
- Account lockout: After 10 failed attempts, require email verification to unlock
- Timing attacks: Always run
password_verify()even if the user doesn't exist (prevents user enumeration via response time)
7. CSRF, Rate Limiting & Bot Mitigation
CSRF Token Implementation
Every form must include a CSRF token. The token is generated server-side, embedded as a hidden field, and verified on submission.
hash_equals(), not == or ===. String comparison operators are vulnerable to timing attacks — an attacker can determine how many characters match by measuring response time. hash_equals() runs in constant time.
Rate Limiting
| Endpoint | Limit | Window |
|---|---|---|
| Login / Register | 5 requests | 60 seconds per IP |
| Contact / Quote forms | 5 submissions | 60 seconds per IP |
| Checkout | 10 requests | 60 seconds per IP |
| Shipping rate lookup | 20 requests | 60 seconds per IP |
| API endpoints (general) | 60 requests | 60 seconds per API key |
| Password reset | 3 requests | 60 seconds per IP |
- Response: Return HTTP 429 (Too Many Requests) with a
Retry-Afterheader - Do not reveal the exact limit in error messages — simply say "Please wait before submitting again"
- Storage: Redis for multi-server coordination, database fallback for single-server
- Pattern: Sliding window (track timestamps per IP) or token bucket (allow bursts within average)
Honeypot Spam Prevention
Bot Detection Layers
Modern bot mitigation uses multiple signals, not just CAPTCHAs:
| Layer | Technique | Catches |
|---|---|---|
| 1. Honeypot | Hidden form field (our standard) | Basic automated bots. Free, zero UX impact. |
| 2. Rate limiting | Per-IP request throttling | Brute force, credential stuffing, spam floods. |
| 3. Behavioral | Mouse movement, keystroke timing, scroll patterns | Headless browsers. Bots move in straight lines; humans don't. |
| 4. Reputation | IP reputation, ASN/hosting detection, TOR exit nodes | Known bot networks, VPN/proxy abuse, data center IPs. |
| 5. Challenge | Cloudflare Turnstile (preferred) or hCaptcha | Sophisticated bots. Use only when layers 1–4 are insufficient. |
8. Payment & PCI DSS
Cardinal Rule
PCI Compliance Levels
| Level | What It Means | Our Target |
|---|---|---|
| SAQ-A | All payment processing is fully outsourced. No card data on your server. | This is us (Stripe Checkout) |
| SAQ-A-EP | Embedded payment fields (Stripe Elements). Card data doesn't touch server, but your page hosts the form. | Acceptable alternative |
| SAQ-D | You handle card data directly. Full PCI audit required. | Never needed for our sites |
Stripe Webhook Security
- Webhook secret: Store in
.env, never in source code - Idempotency: Stripe may retry webhooks. Always check if the event has already been processed before creating orders.
- Timing: Respond within 5 seconds or Stripe will retry. Do heavy processing asynchronously.
9. API & Webhook Security
Admin Endpoint Protection
Every admin API route must verify authentication before processing. No unprotected admin endpoints — ever.
CORS Configuration
JWT Best Practices
If using JWTs (for API-to-API communication, not browser auth where sessions are preferred):
- Algorithm: Use
ES256(ECDSA) orRS256(RSA) for production. NeverHS256with a weak secret. Never allow"alg": "none". - Expiry: Access tokens: 5–15 minutes max. Refresh tokens: rotate on every use.
- Validation: Always validate
algheader against an allowlist server-side. Validateiss,aud, andexpclaims. - Storage: Secrets in a dedicated secrets manager or
.env. Rotate every 90 days.
Error Handling (OWASP A10)
- Never leak: Stack traces, SQL errors, file paths, server versions, environment variables, database names
- Always return: A generic, human-readable error message with an appropriate HTTP status code
- Always log: The full exception with stack trace, request context, and user session to server-side logs
- Fail closed: When an error occurs, deny the action. Never fail open (granting access on error).
10. Supply Chain & Dependency Security
Software supply chain attacks are now OWASP Top 10 #3 (A03:2025). In 2025, compromised npm packages via phished maintainer accounts affected thousands of projects. This is not theoretical.
Subresource Integrity (SRI)
Every script or stylesheet loaded from a CDN must include an integrity attribute. If the CDN is compromised and the file changes, the browser refuses to execute it.
npm Dependency Security
- Always commit
package-lock.jsonto version control - Use
npm ci(notnpm install) in CI/CD — installs exactly from lock file, fails on mismatch - Run
npm auditregularly and integrate into CI pipeline - Pin direct dependencies to exact versions in
package.json - Disable lifecycle scripts:
npm config set ignore-scripts true— prevents maliciouspostinstallscripts - Watch for typosquatting: Verify package names carefully before installing (
lodashvslodash-esvsl0dash) - Enable 2FA on npm maintainer accounts
PHP Dependency Security
- Commit
composer.lockto version control - Use
composer install --no-devin production — excludes dev dependencies - Run
composer auditto check for known vulnerabilities - Never require packages from unverified sources
Secrets Management
.env files only. Never in source code, never in git, never in client-side JavaScript.
.env to .gitignore in every project. Search the codebase before every deploy: grep -r "sk_live\|sk_test\|whsec_\|password" --include="*.php" --include="*.js" .
.env lives in the shared/ directory on the deployment server. It persists across deploys and is never included in the build artifact.
11. DNS Security
DNS is the foundation of your site's identity. If DNS is compromised, attackers can redirect traffic, intercept email, and issue fraudulent SSL certificates.
Required DNS Records
| Record Type | Purpose | Status |
|---|---|---|
| CAA | Restricts which Certificate Authorities can issue SSL certs for your domain | Required. Set to 0 issue "letsencrypt.org" |
| SPF (TXT) | Specifies which mail servers can send email for your domain | Auto-created by dpanel |
| DKIM (TXT) | Cryptographically signs outbound email to prove it's from you | Auto-created by dpanel |
| DMARC (TXT) | Tells receiving servers what to do with email that fails SPF/DKIM | Auto-created by dpanel |
DNSSEC
DNSSEC cryptographically signs DNS records, preventing spoofing and cache poisoning. Without DNSSEC, CAA records are vulnerable to DNS spoofing — an attacker could forge DNS responses to bypass your CAA restrictions and obtain fraudulent certificates.
- Enable DNSSEC on all zones
- Verify DNSSEC validation in PowerDNS configuration
- CAs are now required to validate DNSSEC when checking CAA records (CA/Browser Forum Ballot SC-085v2)
Email Authentication Chain
All three records (SPF + DKIM + DMARC) must be present and aligned. Missing any one creates a gap that phishers exploit:
- SPF alone: Prevents spoofing of the envelope sender but not the From header
- DKIM alone: Proves email integrity but doesn't specify policy
- DMARC: Ties SPF and DKIM together and tells receivers what to do with failures (quarantine or reject)
DNS Monitoring
- Monitor for unauthorized DNS record changes
- Alert on new A/AAAA records, MX changes, or CAA modifications
- Review DNS zone file changes as part of deployment review
12. Server & Runtime Hardening
SSH Configuration
PHP Production Settings
File Permissions
| Path | Permissions | Notes |
|---|---|---|
| Web root directories | 755 | Owner rwx, group/others rx |
| Web files (HTML, CSS, JS, PHP) | 644 | Owner rw, group/others r |
| Config files (.env, wp-config.php) | 400 | Owner read-only. Never 644 or 777. |
| Upload directories | 755 | Never 777. Store uploads outside webroot if possible. |
| Log directories | 750 | Web server group read |
| SSL private keys | 600 | Root/service owner only |
Nginx Security
Firewall Rules
- Default deny inbound: Only allow ports 80 (HTTP), 443 (HTTPS), and SSH
- Rate-limit SSH: CSF or fail2ban to block IPs after failed attempts
- Block known-bad IPs: Subscribe to threat intelligence feeds
- Geo-restrict if applicable: If your audience is US-only, consider blocking known bot-heavy regions
- Separate management traffic: SSH on a non-standard port is defense-in-depth (not security by itself)
13. WAF & Firewall Patterns
A Web Application Firewall inspects HTTP traffic and blocks malicious requests before they reach your application code.
OWASP Core Rule Set (CRS)
The OWASP CRS is the standard WAF rule set, used with ModSecurity or Cloudflare. It blocks:
| Attack Type | What CRS Catches |
|---|---|
| SQL Injection | UNION-based, blind, time-based, parameterized patterns |
| XSS | Reflected, stored, DOM-based script injection patterns |
| File Inclusion (LFI/RFI) | Path traversal (../), remote file includes |
| Remote Code Execution | Command injection, shell metacharacters |
| Scanner Detection | Known vulnerability scanner signatures (Nikto, sqlmap) |
| Protocol Violations | Malformed HTTP, invalid methods, oversized headers |
| Data Leakage | Blocks responses containing error messages, stack traces, sensitive patterns |
Paranoia Levels
- Level 1 (default): Low false positives. Good baseline.
- Level 2: Recommended for production. Catches more attacks with manageable false positives.
- Level 3–4: Maximum detection but high false positives. Only for security-critical applications.
Custom Rules to Add
DetectionOnly mode for 1–2 weeks to identify false positives before switching to blocking mode. Review logs and whitelist legitimate requests that trigger rules.
14. Monitoring, Logging & Incident Response
OWASP A09:2025 was renamed "Security Logging and Alerting Failures" to emphasize: logging without alerting is useless.
What to Log
| Event | Log Data | Alert On |
|---|---|---|
| Authentication events | Timestamp, IP, user ID, success/failure, method | 5+ failed logins in 60 seconds from same IP |
| Authorization failures | Timestamp, IP, user ID, requested resource, reason | Any attempt to access admin endpoints without valid auth |
| Input validation failures | Timestamp, IP, endpoint, rejected input (sanitized) | Injection patterns detected (SQL, XSS, command) |
| Admin actions | Timestamp, admin user, action, affected records | New admin account created, bulk delete, config change |
| Payment events | Timestamp, order ID, amount, status, Stripe event type | Failed webhook verification, unusual refund patterns |
| Application errors | Timestamp, error type, stack trace, request context | Error rate exceeds baseline (5xx spike) |
Logging Rules
- Use structured logging (JSON): Include timestamp, source IP, user ID, action, result, session ID
- Never log: Passwords, full credit card numbers, session tokens, or other secrets
- Centralize logs: Aggregate from all services into a single searchable system
- Retain: Security logs for 1–7 years depending on compliance requirements
- Protect logs: Log files must not be publicly accessible. Permissions:
640
Incident Response Plan
15. Privacy & Compliance (GDPR, CCPA)
GDPR (EU/EEA)
The General Data Protection Regulation applies to any business serving EU/EEA residents, regardless of where the business is located. Maximum penalty: 4% of global annual revenue or €20M, whichever is higher.
Cookie Consent
- Explicit opt-in BEFORE non-essential cookies: Analytics, marketing, and personalization cookies must not be set until the user actively consents. Pre-checked boxes are not compliant.
- Granular consent: Users must accept or reject categories individually (necessary, analytics, marketing).
- Easy withdrawal: Must be as easy to withdraw consent as to give it. Persistent settings link in footer.
- No cookie walls: Cannot block site access if user rejects non-essential cookies.
Data Subject Rights
- Right to access: Users can request a copy of all personal data you hold
- Right to be forgotten: Users can request deletion (unless lawful retention basis exists)
- Data portability: Users can request data in machine-readable format (JSON, CSV)
- Right to rectification: Users can correct inaccurate data
- Breach notification: 72 hours to notify supervisory authority. If high risk to individuals, notify them directly.
CCPA / CPRA (California)
Applies if your business has annual gross revenue over $25M, processes data of 100,000+ California residents, or derives 50%+ revenue from selling personal information.
- "Do Not Sell or Share My Personal Information" link: Must be prominently displayed (typically in footer). Required if any third-party tracking (Facebook Pixel, Google Analytics with advertising features) constitutes "sharing."
- Global Privacy Control (GPC): Must honor the GPC browser signal as a valid opt-out request.
- Right to delete: California residents can request deletion of their personal data.
- No retaliation: Cannot charge different prices or deny services to users who exercise privacy rights.
E-Commerce Privacy Checklist
- Privacy policy linked before any form submission (checkout, registration, contact)
- Marketing opt-in is a separate, unchecked checkbox
- Data retention periods defined and enforced
- Third-party integrations (analytics, payment, shipping APIs) documented
- Account deletion mechanism available
- Data export mechanism available (JSON or CSV)
16. Emerging Threats (2025–2026)
New attack vectors that didn't exist (or weren't practical) two years ago.
AI-Powered Attacks
- Voice cloning: Requires only 3–5 seconds of sample audio. In 2025, a voice clone of the Italian Defense Minister was used to extract nearly €1M. Do not use voice-based verification for sensitive operations.
- Deepfake video: Real-time replication is becoming indistinguishable from reality. Video-based KYC and identity verification are no longer reliable alone.
- AI-generated phishing: More convincing, personalized, and produced at scale with no language errors. Train users to verify sender identity, not just message quality.
Prompt Injection
If your site integrates AI features (chatbots, search, content generation), prompt injection is the #1 risk (OWASP LLM Top 10).
- Direct injection: User crafts input that overrides the AI's system prompt
- Indirect injection: Malicious instructions hidden in external content (emails, web pages) that AI agents process. Example: EchoLeak exfiltrated data from Microsoft Copilot via hidden email instructions — zero clicks required.
- Mitigation: Input/output filtering, sandbox AI agent actions, limit tool access, never treat AI-processed external content as trusted instructions
Supply Chain Escalation
Supply chain attacks elevated to OWASP A03:2025. Key 2025 incidents:
- Shai-Hulud Attack: Self-replicating worm compromised popular npm packages via phished maintainer accounts, injecting malicious postinstall scripts
- September 2025: 18 widely-used npm packages hijacked, publishing obfuscated cryptocurrency theft code. CISA issued a formal advisory.
- Defense: See Section 10 (SRI, npm audit, lock files, lifecycle script protection)
Browser Security Evolution
- TLS 1.3: 76% adoption and growing. TLS 1.2 declining to ~13%. TLS 1.0/1.1 effectively dead.
- Trusted Types: Emerging CSP directive for DOM XSS prevention. Requires typed objects for DOM sink assignments instead of raw strings.
- Private State Tokens: Browser-native trust propagation replacing CAPTCHAs. Not yet widely adopted but on the horizon.
- Third-party cookie partitioning: Firefox partitions by default. Safari blocks entirely. Chrome reversed course on removal but Privacy Sandbox evolves.
17. Pre-Launch Security Checklist
Every item must pass before deploying to production. No exceptions. A single missing item is a launch blocker.
| ☐ | Security Measure | Why It Matters |
|---|---|---|
| ☐ | HTTPS everywhere with HSTS | Encrypts all data in transit. Required by Stripe, expected by customers, rewarded by Google. |
| ☐ | All security headers configured (see Section 2) | CSP, X-Content-Type-Options, X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP. |
| ☐ | CSRF tokens on all forms | Prevents attackers from submitting forms on behalf of other users. |
| ☐ | Parameterized SQL queries (PDO prepared statements) | Prevents SQL injection. Never concatenate user input into SQL. |
| ☐ | Input sanitization (htmlspecialchars) | Prevents XSS — malicious JavaScript injected via form fields. |
| ☐ | Rate limiting on all sensitive endpoints | Prevents brute-force, spam floods, and checkout abuse. |
| ☐ | Honeypot on all public forms | Catches automated bots without degrading UX. Returns 200 on detection. |
| ☐ | Webhook signature verification | Prevents fake webhook events that could create fraudulent orders. |
| ☐ | No card data on server | Stripe Checkout handles it. No card data = PCI SAQ-A (simplest compliance). |
| ☐ | Email validation (filter_var) | Prevents malformed addresses, header injection, bounce storms. |
| ☐ | No secrets in source code | API keys and passwords in .env only. grep -r "sk_live\|sk_test\|whsec_" returns zero matches. |
| ☐ | Generic error messages | No stack traces, SQL errors, or file paths in responses. Detailed errors in logs only. |
| ☐ | Admin endpoints require auth | Every admin API route verifies Bearer token or session auth. |
| ☐ | SRI on all external scripts | Prevents CDN compromise from injecting malicious code. |
| ☐ | CORS origin set to production domain | Not wildcard, not DevHub. Prevents cross-origin API abuse. |
| ☐ | .env file not publicly accessible | Blocked by nginx location ~ /\. rule. |
| ☐ | Debug/display_errors disabled | No PHP errors, stack traces, or debug output visible to users. |
| ☐ | All DevHub URLs purged from source | Search for devhub, preview.ecom. Any remaining cause broken calls and Google indexing. |
| ☐ | CAA record restricts certificate issuance | Only Let's Encrypt can issue certs for your domain. |
| ☐ | SPF, DKIM, DMARC records verified | Prevents email spoofing and phishing using your domain. |
| ☐ | Cookie attributes set (HttpOnly, Secure, SameSite) | Prevents session hijacking via XSS and network sniffing. |
| ☐ | Session regenerated after login | Prevents session fixation attacks. |
| ☐ | Server/X-Powered-By headers removed | Prevents server fingerprinting. |
| ☐ | Sensitive file paths blocked in nginx | .env, .git, .sql, composer.json, package.json all return 403. |
18. Penetration Testing Protocol
Before every production deployment, manually verify security measures work. These are the exact tests to run.
Input & Injection Tests
<script>alert('xss')</script> in every text field. Output must be escaped, not executed. Check the database too — stored XSS is worse than reflected.
' OR 1=1 -- and '; DROP TABLE users; --. Application must handle gracefully (prepared statements prevent this by design).
Auth & Access Control Tests
Payment & Webhook Tests
Information Leakage Tests
grep -r "sk_live\|sk_test\|whsec_\|password.*=.*['\"]" --include="*.php" --include="*.js" . — zero matches expected.
/.env, /.git/config, /composer.json, /api/config.php must all return 403 or 404 — never the file contents.
Server and X-Powered-By must be absent or non-informative. All security headers from Section 2 must be present.
Automated Scan
After manual tests pass, run automated tools:
- Mozilla Observatory:
https://observatory.mozilla.org— grades HTTP headers, TLS, cookies - SecurityHeaders.com: Checks all response headers against best practices
- SSL Labs:
https://www.ssllabs.com/ssltest/— TLS configuration grading - npm audit: Check for known vulnerable dependencies
- OWASP ZAP: Open-source web app security scanner for deeper analysis
19. File Integrity & HTML Monitoring
Automated detection of unauthorized changes to production site files and rendered HTML content. This catches file tampering, database injection, defacement, SEO spam injection, and malware insertion — whether the attacker modifies files directly or manipulates database content that renders into pages.
Architecture: Origin-Only Scanning
Integrity scans run exclusively against the origin server (prod-hetzner), not CDN edge copies. The origin holds the master files — if the origin is clean, edge copies are clean. This keeps the monitoring footprint near-zero and avoids polluting CDN caches or inflating bandwidth.
Layer 1: Deploy-Time File Manifest
At deploy time, generate a SHA-256 checksum of every file in the release. This manifest represents the known-good state and is stored in the deployment’s shared directory.
- What it catches: Any file modified, added, or deleted outside of a deployment — backdoors, web shells, modified PHP, injected JavaScript
- How it works: A cron job (every 5–15 minutes) recalculates checksums of the active release and compares against the manifest
- Manifest format:
sha256sumoutput — one hash per file, stored as.integrity-manifest.sha256 - Exclusions: Log files, upload directories, session files, cache directories — anything expected to change at runtime
- Alert on: Any hash mismatch, any new file not in the manifest, any missing file
Layer 2: Rendered HTML Snapshot Comparison
File integrity alone misses database-level attacks. If an attacker injects a <script> tag into a product description via SQL injection, the PHP files are untouched but the rendered HTML is compromised. HTML snapshot monitoring catches this.
- Baseline: After each successful deployment, crawl every page on the site and store a SHA-256 hash of the rendered HTML
- Scan: Periodically re-crawl and compare hashes against baseline
- Smart filtering: Strip dynamic content before hashing — CSRF tokens, session IDs, timestamps, nonces, cache-busting query strings. Only hash the structural content.
- Alert on: Any page whose structural hash changes without a corresponding deployment
- Ignore: Product price changes pushed through the admin (these trigger a new baseline via the deploy/sync pipeline)
Implementation: THR Hub Integration
The THR Hub already monitors multiple sites (health checks, scoresheets, sync status). File integrity becomes another monitoring dimension:
- Hub dashboard card: Per-site integrity status — last scan time, pass/fail, number of changes detected
- API endpoint:
/api/integrity-check.phpon each site — recalculates file hashes, compares against manifest, returns JSON report - Centralized scanning: Hub polls each site’s integrity endpoint on a schedule — no per-site cron configuration needed
- Alert channels: Email notification to site admin on any integrity failure
- Manifest size: ~5KB per site (64 chars per hash × number of files). Negligible storage impact.
What This Detects
| Attack Type | File Manifest | HTML Snapshot |
|---|---|---|
| Web shell uploaded | ✓ | — |
| PHP file modified (backdoor) | ✓ | ✓ |
| JavaScript injection (file-level) | ✓ | ✓ |
| SQL injection → malicious content in DB | — | ✓ |
| SEO spam links injected via DB | — | ✓ |
| Site defacement | ✓ | ✓ |
| Hidden redirect added | ✓ | ✓ |
| Product price manipulation (DB) | — | ✓ |
Scan Frequency Guidelines
- File manifest check: Every 5–15 minutes (lightweight — just hash comparison)
- HTML snapshot check: Every 1–4 hours (heavier — requires HTTP requests to every page)
- Post-deploy: Both checks run immediately after any deployment to establish new baseline
- On-demand: Admin can trigger a full scan from the Hub dashboard at any time
20. Admin Authentication Tiers
Three distinct authentication tiers enforce the principle of least privilege across the platform. Each tier has progressively stricter security requirements based on the scope of access granted.
Tier 1: Master Admin (Hub)
Full cross-site access. User management, system settings, deployment controls, all site data.
- Authentication: Bcrypt session auth (portal login) + mandatory TOTP two-factor
- Session timeout: 30 minutes of inactivity — every API call, page load, or action resets the timer. Active sessions never expire mid-work.
- Login lockout: 5 failed attempts = 15-minute lockout + email alert to master admin
- IP logging: Every login records IP address, user agent, and timestamp
- Audit trail: Every action logged — what changed, who changed it, when, from where
- IP allowlisting: Optional — restrict master admin login to known IP ranges for maximum lockdown
- Unique privilege: Only master admin can create/modify other admin accounts, reset passwords, access system settings, and impersonate lower-tier accounts for debugging
Tier 2: Site Admin (Per-Site Dashboards)
Scoped to a single site. Manages products, orders, members, and site-specific settings. Cannot access other sites or system-level controls.
- Authentication: Bcrypt session auth (replaces static
X-Admin-Tokenpattern) - Two-factor: Optional — recommended but not mandatory (reduces friction for staff/clients)
- Session timeout: 60 minutes of inactivity — resets on every action, same sliding-window pattern as Tier 1
- Scope enforcement: A PC admin can only see PC data. Server-side checks verify site scope on every API call — never trust the client to enforce boundaries.
- Role-based access within site:
- Manager: Full CRUD on products, orders, members within that site
- Viewer: Read-only access — can view orders, products, reports but cannot modify
- Cannot: Create admins, reset other admin passwords, access API keys, modify site settings, or view other sites
Tier 3: Client Portal User
End-user access. Views their own orders, products, and account information. No administrative capabilities.
- Authentication: Bcrypt session auth (already implemented in portal)
- Two-factor: Not required
- Session timeout: 120 minutes of inactivity (relaxed — these users browse casually)
- Data isolation: Users can only see data linked to their own account/client ID. Server-side filtering — never client-side.
- Cannot: Access admin dashboards, view other users’ data, modify products/pricing, or perform any administrative action
Session Timeout Protocol: Sliding Window
All session timeouts use a sliding window (inactivity-based) model, not absolute expiration. The timer resets on every authenticated action:
- Any API call with a valid session → resets the timer
- Any page load that checks auth → resets the timer
- Any form submission → resets the timer
- No action for the timeout duration → session invalidated server-side, user must re-authenticate
Implementation: store last_activity timestamp on the session record. On every authenticated request, compare NOW() - last_activity against the tier’s timeout threshold. If expired, destroy the session and return 401. If valid, update last_activity to now.
Two-Factor Authentication: TOTP
Time-based One-Time Password (RFC 6238) is the recommended 2FA method. Compatible with Google Authenticator, Authy, 1Password, and all standard TOTP apps.
- Setup: Master admin scans a QR code (otpauth:// URI) during account setup. Secret stored server-side, hashed.
- Login flow: Password verified first → if correct, prompt for 6-digit TOTP code → verify against server time (±1 window for clock drift)
- Backup codes: Generate 10 single-use recovery codes at setup. Stored hashed. Each code works once.
- Fallback: Email OTP as emergency fallback if authenticator app is lost. Sends 6-digit code to registered email, valid for 10 minutes.
2FA Method Comparison
| Method | Security | UX Friction | Cost | Recommendation |
|---|---|---|---|---|
| TOTP (Authenticator app) | Strong | Low | Free | Primary — use this |
| Email OTP | Moderate | Medium | Free | Emergency fallback only |
| WebAuthn / Passkeys | Strongest (phishing-proof) | Lowest | Free | Future state — add when browser support matures |
| SMS OTP | Weak (SIM swap attacks) | Medium | Per-message | Do not use |
Migration Path: Replacing Static Tokens
The current PC and THR admin dashboards use a static X-Admin-Token header. This must be replaced:
| Current Pattern | Target Pattern |
|---|---|
Static X-Admin-Token header | Session cookie + bcrypt login |
| Same token for all users | Per-user credentials with role assignment |
| Token never expires | Sliding-window inactivity timeout |
| No audit trail | Every admin action logged with user, IP, timestamp |
| No 2FA | TOTP mandatory for Tier 1, optional for Tier 2 |
| No revocation | Sessions can be killed individually or globally |