Security
57 articles in this topic.
Exactly-once over an unreliable pipe: HMAC, nonces, and a byte-exact contract
A per-request nonce and a per-content hash look like the same idea and are not. One says this request arrived twice, the other says this message arrived twice, and a payment pipeline needs both. The signing design behind a production SMS gateway.
Deploying Frappe apps to a client server you're not allowed to touch
The client will not give you access to their dev server, so every code change means someone on their side pulls and migrates by hand. Here is the complete fix: a self-hosted runner they install, a deploy script they own, and no credential to their environment on your side at all.
Bolting a phone onto ERPNext: custom routes, guest endpoints and HMAC auth in Frappe
Frappe claims every /api/* path before website routing, so a fixed third-party path 404s. Here is the before_request rewrite that fixes it, including the cached property you also have to bust, and why allow_guest endpoints can still be strongly authenticated.
Push access is code execution: hardening a self-hosted runner
A self-hosted runner executes whatever the workflow file says, and the workflow file lives in a repository you control. So push access to your repository is command execution on your client's server. Here is the honest risk and the layers that contain it.
Secrets, environments and required reviewers
A repository secret is readable by everyone who can push to the repository. An environment secret is not released until a gate opens. Understanding that difference is what lets you hand the client the key to their own deployments.
Access is a liability, not a convenience
Every SSH key you hold on a client's server is a credential you now have to defend, insure and explain. The strongest position in an enterprise procurement conversation is not having it at all.
Blocked by CORS Policy: Stop Reaching for Access-Control-Allow-Origin: *
The wildcard breaks the moment you send cookies, and reflecting the Origin header is worse. Here is the allowlist pattern, the nginx config and the curl test that proves it.
Your Cert Automation Should Default to Let's Encrypt Staging
Five test renewals can exhaust the duplicate certificate limit and block a real renewal for a week. Here is why certbot drifts to production, and the directory split that prevents it.
Certbot connection refused on http-01: diagnosing proxies, IPv6 and port 80
Connection refused is not the same as a timeout, and the difference tells you exactly where to look. A diagnostic order for failed Let's Encrypt http-01 challenges.
The agent drafts, the human commits: designing the write path for a business AI
A wrong read costs minutes. A wrong write costs a reconciliation. So the commit lives behind an HTTP endpoint the agent cannot call - and everything it can do is scoped to the requesting user, written to an audit row in the same transaction, and given a tested reversal.
The Security Checklist Every Kenyan SME Running Its Own Server Should Finish This Week
You do not have a security budget problem, you have a completeness problem. Ten free controls with real commands, a cost table, and the weekly rhythm that keeps a self-hosted Kenyan SME system out of trouble.
Kenya's Data Protection Act Is an Engineering Spec: What Sections 25, 31, 41 and 43 Demand From Your Code
The DPA 2019 is not a lawyer problem, it is a schema, retention and logging problem. Registration thresholds, the 72-hour breach clock, the encryption safe harbour, and the eight things to build this quarter.
How Kenyan Small Businesses Actually Get Hacked (Nobody Is Burning a Zero-Day on Your Duka)
Five real attack chains account for almost every SME breach: infostealers, credential stuffing, exposed admin panels, unpatched plugins and invoice fraud. With the log commands to check each one today.
Stop Committing Keys: A Secrets Workflow for Two-Person Teams Using SOPS and age
You do not need Vault. You need encrypted secrets in git, one age key per human, and a pre-commit hook that refuses the mistake. Full setup, offboarding in two commands, and when to actually upgrade.
Certbot http-01 Fails Behind Your Force-HTTPS Redirect: It Is Not HSTS
Let's Encrypt follows your 301 to HTTPS and ignores HSTS entirely. So the real cause of a failed http-01 renewal is one of four things, and disabling HSTS is not the fix.
Certbot renewal fails silently: alerting before the certificate expires
Certbot can fail twice a day for three months without telling anyone, and Let's Encrypt stopped sending expiry emails in 2025. Three independent alerts that catch it, with the tests.
CSP 'unsafe-inline' Is Not a Fix: Nonces That Work in Next.js
Adding 'unsafe-inline' to silence CSP violations turns the header into compliance theatre. Here is the per-request nonce setup for Next.js that keeps hydration working.
Surviving a Bot Flood on a $20 VPS: nginx limit_req, fail2ban and Cloudflare Free in the Right Order
Diagnose before you block. A cost-ordered defence for small servers: Cloudflare's single free rate limit rule, nginx limit_req zones that do not break real browsers, fail2ban jails, and stale-while-updating caching.
kubectl Unauthorized on EKS: Check the IAM Identity, Not RBAC
EKS returns Unauthorized when it cannot map your IAM principal to a Kubernetes user, so no ClusterRoleBinding will fix it. Here is the diagnosis order that actually resolves it.
Storing JWTs in localStorage Turns Any XSS Into Account Takeover
Every JWT tutorial puts the token in localStorage. Here is what that costs when XSS lands, and the in-memory access token plus HttpOnly refresh cookie setup that replaces it.
Kubernetes Has No Cross-Namespace Secrets: Why Manual Copies Always Drift
A Pod cannot reference a Secret in another namespace and Kubernetes will not sync copies for you. Here is why, and the three mechanisms that actually hold up.
"Too Many Certificates Already Issued": Fixing Let's Encrypt Rate Limits
The duplicate certificate limit is 5 per exact name set per week and global across all accounts, so switching clients cannot help. Here is what actually works.
Users Randomly Logged Out: Fixing next-auth Token Rotation
Auth.js refresh token rotation silently drops the rotated token in four distinct ways, and the popular workaround of disabling rotation is worse than the bug. Here is the real fix.
npm audit Noise: Triaging CVEs Without Turning Off the Alarm
npm audit tells you a version matched an advisory range, not that you are exploitable. A four-gate triage that ends in a written decision instead of a disabled CI step.
'Unable to get local issuer certificate': fix the chain, not the client
The site works in Chrome and fails everywhere else. That is an incomplete nginx certificate chain, and the fix is fullchain.pem, not curl -k or disabling verification.
ACME TLS Handshake Failure Behind a Load Balancer: When Only Some Backends Answer
Cert renewals fail intermittently behind a load balancer while normal traffic is fine. The cause is not certbot; it is inconsistent backends. Here is the diagnosis and fix.
Your Webhook Endpoint Trusts Anyone Who Can curl It
Skipping HMAC verification turns a webhook into an unauthenticated database write. Why verification breaks when you finally add it, and the raw-body fix for Stripe, GitHub and Shopify.
Auth.js v5 redirects to the wrong URL behind a reverse proxy
Set NEXTAUTH_URL and it still redirects to localhost or the container IP. In v5 the host comes from proxy headers gated by trustHost, and that is the real fix.
Certbot Keeps Making New Certificates Instead of Renewing: --cert-name Explained
Certbot identifies a certificate by its lineage name, not its domain list. Here is why it spawns duplicate -0001 certs and how --cert-name fixes it for good.
Your DDNS subdomain keeps hitting Let's Encrypt rate limits, and it is not a bug
If certificates fail on a shared DDNS suffix like duckdns.org, you are sharing one rate-limit bucket with every other customer. Here is why, and the only real fix.
Your fail2ban Stopped Banning Anyone and Never Told You
fail2ban fails silently: a dead filter looks exactly like a quiet internet. Here is the post-upgrade check that proves your jails still work, and how to alert on zero bans.
The HSTS Preload List Is Almost One-Way: Check This Before You Submit
Preloading HSTS hardcodes your domain into browsers with no server-side undo. Why every subdomain and expired cert becomes a forever problem, and the checklist to run before you commit.
The GitHub Actions Pwn Request: How Fork PRs Steal Your Secrets
pull_request_target plus a fork checkout hands your CI secrets to any attacker. Here is the mechanism, the tj-actions lesson, and the two-workflow fix.
IP Rate Limiting Won't Stop Credential Stuffing: What Actually Does
Distributed credential stuffing rotates thousands of IPs, each trying once, sliding under every per-IP limit. Here is the per-account, breached-password and MFA stack that stops it.
JWTs Have No Logout: Adding Expiry and Revocation Without Losing Statelessness
A JWT you cannot revoke is a password you cannot change. How to add real logout, short-lived access tokens and a revocable refresh token without throwing away statelessness.
JWT alg:none and RS256/HS256 Confusion: Pin Your Algorithms
A JWT verifier that reads the alg header from an untrusted token is not verifying anything. How alg:none and the RS256-to-HS256 swap work, and the pinning fix in five languages.
Let's Encrypt Renewal Fails Behind Cloudflare Proxy: Use DNS-01, Not http-01
Cloudflare's orange-cloud proxy breaks http-01 ACME challenges. You do not have to grey-cloud your site. Switch to DNS-01 over Cloudflare's API and renew with the proxy on.
useSession() Only Updates After a Manual Reload in the Next.js App Router
NextAuth useSession() stays stale until you reload because the App Router has two session reads. Here is why router.refresh() only fixes half, and the reliable pattern.
Webhook Signature Verification Fails Randomly? It Is Your Body Parser Order
A webhook signature check that passes on some events and fails on others is not a secret problem. Why re-serialized JSON breaks the HMAC, and how to verify the raw body before parsing.
Certbot Bypasses systemd for Nginx: Why Renewals Leave Them Out of Sync
Certbot's nginx plugin signals nginx directly instead of via systemctl, so systemd's state drifts from reality after renewal. Here is the PID-file race and the fix.
Stop Using chmod 777: What to Do Instead When You Get Permission Denied
chmod 777 makes a file world-writable and turns an uploads folder into a remote code execution path. Here is how to actually diagnose Permission denied and fix it with chown and 755/644.
When Certbot's nginx Plugin Mangles Your Server Block, Fix It by Hand
Re-running certbot --nginx to fix a broken server block usually makes duplicate, conflicting blocks. Use certonly and wire the SSL directives in yourself.
DNS-01 Fails but the TXT Record Is There: Delegate _acme-challenge
DNS-01 validation keeps failing with NXDOMAIN even though your TXT record exists. It is a delegation problem, not propagation. Delegate _acme-challenge via CNAME to fix it for good.
FastAPI Cross-Site Cookies Silently Not Set: SameSite and Secure
Your FastAPI set_cookie returns 200 but the browser never stores it and there is no error. Here is why cross-site cookies need SameSite=None; Secure, and how to fix it.
FastAPI CORS Silently Failing: the allow_credentials Plus Wildcard Trap
FastAPI CORS with allow_credentials=True and allow_origins=['*'] passes testing then breaks once cookies flow. The browser blocks it silently. Here is why and the fix.
A Valid JWT Signature Isn't Enough: Check the aud and iss Claims
A valid JWT signature proves who issued the token, not who it was for. Without aud and iss checks, one service's token works against another. Here is the fix.
Kubernetes Secrets Are base64, Not Encrypted: What Actually Protects Them
A Kubernetes Secret is base64-encoded plaintext, not a vault. Here is what actually protects it: encryption at rest, tight RBAC, and an external secrets manager.
Let's Encrypt Renewal Fails Behind Cloudflare Proxy: Use DNS-01, Not http-01
Cloudflare's orange-cloud proxy breaks http-01 ACME challenges. You do not have to grey-cloud your site. Switch to DNS-01 over Cloudflare's API and renew with the proxy on.
Laravel 419 Page Expired in Production: A Diagnostic Order, Not a Fix List
The intermittent Laravel 419 TokenMismatchException has five common causes that look identical. Here is the order to diagnose them so you fix it once.
Two ACME Clients, One Domain: Handling Multiple _acme-challenge Records Safely
When two ACME clients share a domain, DNS-01 renewals fail intermittently and people delete each other's TXT records. Here is why a name holds many TXT records and how to make the clients coexist.
Multi-tenant next-auth: fixing NEXTAUTH_URL without disabling host checks
NEXTAUTH_URL is a single value read once at boot, so multi-domain apps redirect to the wrong host. The workaround everyone posts reopens host-header injection. Here is the safe pattern.
Certificates for a Multi-Server Cluster: Issue Once, Distribute Everywhere
Running certbot on every node for the same domain burns your Let's Encrypt rate limit and splits your cluster's identity. Issue once with DNS-01 and distribute instead.
Anyone Can Forge X-Forwarded-For: Configuring nginx real_ip Correctly
A raw client request can claim any source IP in X-Forwarded-For. Here is how to scope nginx set_real_ip_from and real_ip_recursive so your rate limits and allowlists trust a verified IP.
Old Android and OpenSSL Clients Failing TLS After the Root CA Transition
TLS works in every browser but fails on old Android and ancient OpenSSL. It is a client trust-store issue, and the DST Root CA X3 workaround is now dead.
Postgres 'no pg_hba.conf entry for host': the scoped fix, not 0.0.0.0/0
The FATAL: no pg_hba.conf entry error has a two-minute fix that also opens your database to the whole internet. Here is the scoped rule to add instead.
6-Day Certificates Are Here: Auditing Renewal Automation That Assumed 90 Days
Let's Encrypt short-lived certs live 160 hours, not 90 days. Your weekly cron and 30-day expiry alert are now an outage. Here is the audit that fixes both.
Free HTTPS on Nginx With Let's Encrypt, and the Renewal Everyone Forgets
Getting a free Let's Encrypt certificate on Nginx takes five minutes. Keeping it alive for the next three years is the part that breaks. Here is the issue, the config and the renewal.