slop-stuff cheatsheets & experiments git

slop-stuff / Security

Security practices

Defend like it's always attacked.

Auth, secrets, supply chain, hardening, web security, and incident response.

defensiveauthsecretshardening

Authentication, secrets, supply chain, web security, and incident response — the habits and commands that keep a system standing under pressure.

Quick reference

Seven habits that stop most attacks, plus three that keep you patched and visible. The sections below add the how.

  • CIA triad — Confidentiality (encrypt, control access), Integrity (hash, sign), Availability (redundancy, rate limits).
  • argon2id / bcrypt — Hash passwords with argon2id (OWASP first choice) or bcrypt cost 12+ — never MD5 / SHA-1 / plaintext.
  • MFA — Require a second factor (passkey / TOTP) for every human account — the biggest lever against account takeover.
  • Least privilege — Non-root, read-only, scoped IAM, short-lived tokens. Grant the minimum a task needs.
  • Secrets management — Vault-managed and runtime-injected; never in git, env dumps, or frontends. Rotate before you leak.
  • OWASP Top 10 — The 2025 baseline for web risk — broken access control stays #1; SSRF now folds into it.
  • TLS — TLS 1.3 preferred, 1.2 minimum, HSTS on. Encrypt in transit everywhere.
  • Defense in depth — Layer independent controls so one failure isn’t a full compromise. Assume breach.
  • SBOM + scan — Pin dependencies, generate an SBOM, scan for CVEs in CI (trivy / osv-scanner / npm audit).
  • Rate limit — Throttle login and token endpoints to blunt brute force, stuffing, and scraping.

Security fundamentals

Four ideas underpin every secure system: the CIA triad, threat modeling, least privilege, and defense in depth.

1. CIA triad

Confidentiality (encryption, access control), Integrity (hashing, signatures), Availability (redundancy, rate limits).

2. Threat modeling

Map assets, entry points, and trust boundaries. Use STRIDE or attack trees before writing code.

3. Least privilege

Grant the minimum access a task needs — scoped IAM roles, short-lived tokens, narrow permissions.

4. Defense in depth

Layer independent controls (network, app, data) so a single failure isn’t a full compromise.

KEY: Assume breach. Design so a single compromised credential, host, or dependency does not hand an attacker the whole system.

Authentication & authorization

Prove who someone is (authN), then decide what they may do (authZ). Never store passwords in plaintext.

Password hashingWork factorUse whenNotes
bcryptcost 12+Default for web appsSlow, salted; 72-byte input limit.
argon2idmemory + time + lanesOWASP first choicePHC winner; resists GPU cracking.
scryptN, r, pMemory-hard alternativeCommon in crypto libraries.
PBKDF2600k+ iterationsFIPS / legacyOlder but fine at high iterations.
MD5 / SHA-1 / plainNeverFast hashes crack at scale.

bcrypt cost 10 = ~100 ms cost 12 = ~400 ms argon2id = memory-hard

Hash + verify (Node)

const hash = await bcrypt.hash(pw, 12);
const ok = await bcrypt.compare(pw, hash);

Hash (argon2 CLI)

echo -n "p@ss" | argon2 salt \
  -id -t 3 -m 65536 -p 4

MFA

Require a second factor: TOTP app, WebAuthn passkey, or backup codes. Enroll admins and finance first.

OAuth 2.0 / OIDC

Use the authorization code + PKCE flow for apps. OIDC layers identity (ID token + /userinfo) on top of OAuth.

JWT

Signed, not secret. Verify alg, exp, aud, and signature; keep tokens short-lived and reject alg=none.

Sessions

Server-side sessions with cookies flagged HttpOnly; Secure; SameSite=Lax. Rotate the session id on login.

User (browser) → App (client + PKCE) → IdP (authorize + token) → API (validates JWT)

JWT anatomy & verification

A JWT is header.payload.signature, each part base64url-encoded. Never trust it without verifying the signature and claims:

# header:  {"alg":"HS256","typ":"JWT"}
# payload: {"sub":"42","exp":1710000000,"aud":"api"}
# verify:  signature, exp (not expired),
#          aud (for your API), iss (trusted)
  • RBAC — roles → permissions. “Admin can delete users”.
  • ABAC — attributes → policy. “Delete if resource.owner == user”.
  • 2FA / MFA — something you know + have + are.
  • PKCE — proof key for code exchange — OAuth for public clients.

Secrets & keys

Credentials live in a secret manager, never in source control. Rotate before you leak.

!: A secret pushed to git is compromised forever. Treat repo history as public: rotate the credential immediately, then scrub with git filter-repo or bfg — rotation always comes first.

Env vars

Fine for local dev, but they leak into child processes, crash dumps, and CI logs.

export DATABASE_URL=postgres://…
node app.js

Vaults / secret managers

Central store with audit, rotation, and access control. Inject at runtime, not build time.

vault kv get secret/app/db
aws secretsmanager get-secret-value \
  --secret-id app/db
ToolKindRotationUse for
HashiCorp Vaultself-hosted vaultdynamic secretsplatform-wide secrets + PKI
AWS Secrets Managermanaged vaultbuilt-inAWS workloads
GCP Secret Managermanaged vaultversionedGCP workloads
SOPSencrypted filesmanualgit-friendly config secrets
Doppler / Infisicalmanaged vaultbuilt-indev → prod sync
  • echo ".env" >> .gitignore — keep .env, *.pem, and *.key out of git.
  • gitleaks detect --source . — scan history for committed secrets.
  • trufflehog git file://. --only-verified — live-verify leaked credentials.
  • git filter-repo --path .env --invert-paths — scrub a file from history (then rotate).

Patterns that must never be committed

aws_access_key_id AKIA… ghp_… sk_live_… xoxb-… -----BEGIN PRIVATE KEY-----

Supply chain

Your app ships other people’s code. Pin it, hash it, and know what’s inside.

Pin & lock

Commit lockfiles and pin exact versions in production. Avoid ^ / ~ ranges for deploys.

package-lock.json   npm
yarn.lock           yarn
Cargo.lock          rust
go.sum               go
Pipfile.lock         python

SBOM

Ship a machine-readable bill of materials (SPDX / CycloneDX) with every release.

syft dir:. -o spdx-json > sbom.json
syft image:app:latest -o cyclonedx-json

Signed commits

Sign commits and tags so history is verifiable. Enable SSH or GPG signing.

git config commit.gpgsign true
git config user.signingkey KEYID
  • npm audit --omit=dev — known CVEs in your JS deps.
  • pip-audit — audit Python environments.
  • osv-scanner -r . — scan many ecosystems via OSV.
  • trivy fs . — scan repo, containers, and IaC for CVEs and misconfigs.
  • govulncheck ./... — Go-specific vulnerability analysis.
  • Dependabot / Renovate — automated PRs for outdated, vulnerable deps.
Dependency hygiene checklist

Pin

npm ci        # exact lockfile install
pip install --require-hashes -r reqs.txt

Scan

npm audit --omit=dev
osv-scanner -r .

✓: Lockfiles are not optional in CI. Run npm ci, pip install --require-hashes, and go mod verify so builds use the pinned, hashed dependency graph.

Hardening

Shrink the attack surface: remove what you don’t need, lock down what remains, patch fast.

Containers non-root

Drop root, use a read-only filesystem, and drop capabilities.

FROM node:20-alpine
USER 10001
COPY --chown=10001 . /app
# run: --read-only --cap-drop ALL

TLS everywhere

TLS 1.3 preferred, 1.2 minimum, HSTS, automate certs. Never terminate at the edge without re-encrypting.

Strict-Transport-Security:
  max-age=31536000; includeSubDomains

SSH hardening

Keys only, no root login, minimal ciphers.

# /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
AllowUsers deploy
PracticeWhat it meansQuick win
Least privilegeminimal perms on users, roles, processesrun as non-root, scope IAM to resources
Network segmentationisolate tiers; deny by defaultsecurity groups / subnets per tier
Patch managementapply security updates on a cadenceauto OS + dependency updates, track CVEs
CIS benchmarksconfiguration baselinesrun a CIS scanner, apply Level 1
Disable unusedturn off ports, services, featuresremove debug endpoints and unused deps

Containers

  • Run as non-root — USER 10001
  • Read-only rootfs — –read-only
  • Drop capabilities — –cap-drop ALL

Linux

  • Disable root SSH — PermitRootLogin no
  • Keys only — PasswordAuthentication no
  • Brute-force lockout — fail2ban maxretry 5

Network

  • TLS version — 1.3 / 1.2 min
  • HSTS max-age — 31536000
  • Firewall default — deny

⌁: CIS benchmarks (cisecurity.org) give step-by-step baselines for Linux, Windows, cloud, and Kubernetes. Start with Level 1 — safe for most workloads.

Web & API security

Assume every request is hostile: validate input, set headers, and limit abuse.

OWASP Top 10 (2025)RiskCore defense
A01 Broken access controlusers reach others’ data; now includes SSRF, BOLA/BFLAauthorize every request server-side, deny by default
A02 Security misconfigurationdefaults, debug, verbose errors, open bucketsharden, disable unused, scan IaC + headers
A03 Software supply chain failurescompromised deps, build, CI/CDSBOM, pin + sign + scan, trusted sources
A04 Cryptographic failuresdata exposed in transit/at restTLS 1.3/1.2, argon2id, strong keys
A05 InjectionSQL, OS, LDAP, XSSparameterized queries, output-encode
A06 Insecure designflaws baked into architecturethreat modeling, fail closed
A07 Authentication failurescredential stuffing, weak MFA, sessionsMFA, rate-limit, breached-password checks
A08 Software/data integrity failuresunsigned updates, insecure deserializationsign artifacts, verify integrity
A09 Logging & alerting failuresno visibility or response to attacksstructured logs + alerting + playbooks
A10 Mishandling of exceptional conditionsfail-open logic, verbose errorsfail closed, centralized error handling

Content-Security-Policy

Restrict what the browser may load and execute — the single best header against XSS.

Content-Security-Policy: default-src 'self';
  script-src 'self'; object-src 'none';
  frame-ancestors 'none'

CORS

CORS relaxes the same-origin policy — only for the origins you actually need, with explicit methods.

Access-Control-Allow-Origin: https://app.example.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Credentials: true

SQL injection → parameterize

Never concatenate user input into SQL. Use bound parameters everywhere.

cursor.execute(
  "SELECT * FROM users WHERE id = %s",
  (user_id,))

XSS → escape output

Context-aware output encoding; sanitize HTML with DOMPurify; prefer templates that auto-escape.

// Go html/template escapes by default
tmpl.Execute(w, userInput)
HeaderWhat it doesExample
Content-Security-Policyallowlist of script/style sourcesdefault-src 'self'
Strict-Transport-Securityforce HTTPSmax-age=31536000
X-Frame-Optionsblock clickjacking (legacy; prefer CSP frame-ancestors)DENY
Referrer-Policylimit referrer leakagestrict-origin-when-cross-origin
Permissions-Policydisable camera/mic/geolocationgeolocation=()
X-Content-Type-Optionsstop MIME sniffingnosniff
The four classics, one line each

SQLi

User input becomes SQL. Fix: bound parameters, never string concat.

XSS

User input becomes HTML/JS. Fix: output-encode, CSP, DOMPurify.

CSRF

Cross-site requests ride the victim’s cookies. Fix: SameSite + CSRF token.

SSRF

Server fetches attacker-controlled URLs. Fix: allowlist, block metadata IPs.

✓: Rate limiting is the cheap fix for brute force, scraping, and DDoS: express-rate-limit, nginx limit_req, or an API gateway throttle — especially on login and token endpoints.

Incident response

Plan before the breach: detect, contain, eradicate, recover — then learn from it.

  1. Prepare — Playbooks, contacts, and tooling ready before you need them. Run tabletop exercises.
  2. Detect — Alerts from SIEM, IDS, and logs. Confirm the scope: what, when, which systems.
  3. Contain — Isolate hosts, revoke keys, disable accounts, snapshot evidence. Stop the bleed first.
  4. Eradicate — Remove the foothold: patch, reimage, rotate secrets, delete persistence.
  5. Recover — Restore from clean backups, monitor for recurrence, bring services back gradually.
  6. Learn — Post-mortem: what failed, what slowed you, and one concrete change to prevent a repeat.
  • healthy — Serving traffic with normal telemetry.
  • degraded — Serving but failing checks — investigate now.
  • contained — Isolated from the network, no egress.
  • restored — Rebuilt from clean image, back in rotation.

Logging that helps

Structured, centralized, tamper-evident. Log auth events, admin actions, and failures — never secrets or PII.

{"ts":"…","evt":"login_failed",
 "user":"ada","ip":"203.0.113.7"}

Backups = 3-2-1

Three copies, two media, one offsite. Test restores — an untested backup is a hope, not a plan.

# restore drill, not just a copy job
pg_restore -d app latest.dump

!: Don’t destroy evidence during containment. Capture memory, disk images, and logs before reimaging a compromised host — you’ll need them for the post-mortem and any legal follow-up.

Pitfalls

The mistakes that keep showing up in breach reports — and how to avoid them.

Hardcoded secrets

API keys and passwords in code, config, or frontends are the #1 breach cause. Move them to a vault and rotate.

# bad
API_KEY = "sk_live_4x…"
# good
API_KEY = os.environ["API_KEY"]

Roll-your-own crypto

Hand-written ciphers and homegrown token formats break quietly. Use vetted libraries and standard primitives.

# use libsodium / WebCrypto / Go crypto
# never invent AES modes or "encoding"

Over-permissioned IAM

Admin access for every service means one leak = total control. Least privilege, short-lived, scoped to resource.

"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::app/*"]

Logging PII & secrets

Logs capture tokens, passwords, and user data, then leak via SIEM or support tickets. Redact by default.

log.info("login", user=u.id)   # not u.email
# redact: mask tokens, never log bodies

Missing MFA

Single-factor accounts fall to phishing and credential stuffing. MFA is the biggest lever against account takeover.

# require for all human users
# hardware keys > TOTP > SMS

curl … | bash

Piping a remote script to a shell skips review and runs as you. Download, inspect, verify, then run.

curl -fsSL URL -o install.sh
sha256sum install.sh   # verify
bash install.sh

Default credentials

Ship with the vendor’s default login and scanners will find you in minutes. Change on first boot; rotate regularly.

# change before exposing anything
admin / admin
root / password

bcrypt’s 72-byte limit

bcrypt silently truncates input after 72 bytes, so a 100-char passphrase and its 72-byte prefix hash the same. Pre-hash with SHA-384, or use argon2id for long passphrases.

# argon2id has no 72-byte ceiling
# or: bcrypt(sha384(pw)) for long inputs

Forced password rotation

Scheduled rotation pushes users to weak, predictable passwords. NIST 800-63B: drop expiry, check new passwords against breached lists, rotate only on compromise.

# check pwned passwords (k-anonymity)
# rotate on breach, not on a calendar

Non-constant-time comparison

Comparing tokens or MACs with == leaks how many bytes match via timing. Use a constant-time compare for secrets.

crypto.timingSafeEqual(a, b)   # Node
hashlib.compare_digest(a, b)   # Python