StackPractices
intermediate By Mathias Paulenko

Security Best Practices Guide

A thorough guide to application security: authentication, authorization, input validation, secrets management, and common vulnerability prevention.

Overview

Security is not a feature you add later—it is a foundation you build into every layer of your application. The following guide covers the essential practices for building secure software.

When to Apply

  • Building any application that handles user data
  • Processing payments or sensitive information
  • Exposing APIs to the internet
  • Working in regulated industries (healthcare, finance, etc.)

Authentication & Authorization

Use Proven Authentication Libraries

Never roll your own authentication. Use battle-tested libraries:

LanguageRecommended Library
Node.jsPassport.js, Auth.js
PythonDjango Auth, Flask-Login
JavaSpring Security, OAuth2
Gocasbin, gorilla/sessions

Multi-Factor Authentication (MFA)

Require MFA for:

  • Admin accounts
  • Production access
  • Financial operations

Authorization Patterns

Role-Based Access Control (RBAC)

User -> Role -> Permission -> Resource

Principle of Least Privilege

Grant only the permissions necessary for each role. Audit permissions quarterly.

Input Validation

Validate at the Boundary

# Python with Pydantic
from pydantic import BaseModel, EmailStr, constr

class CreateUserRequest(BaseModel):
    email: EmailStr
    password: constr(min_length=12)
    role: constr(pattern=r'^(user|admin)$')

Sanitize Output

  • Use parameterized queries (never string concatenation)
  • Escape HTML before rendering in browsers
  • Encode JSON safely

Secrets Management

Never Hardcode Secrets

# ❌ Bad
API_KEY = "sk-live-abc123"

# ✅ Good
API_KEY = os.environ.get("API_KEY")

Use a Secrets Manager

ToolUse Case
HashiCorp VaultEnterprise, complex policies
AWS Secrets ManagerAWS-native applications
Azure Key VaultAzure-native applications
DopplerMulti-cloud, developer-friendly
1Password SecretsSmall teams, simple setup

Dependency Security

Keep Dependencies Updated

# Scan for vulnerabilities
npm audit
pip-audit
snyk test

Lock Files

Always commit lock files (package-lock.json, poetry.lock, Cargo.lock) to ensure reproducible builds.

OWASP Top 10 Prevention

VulnerabilityPrevention
InjectionParameterized queries, input validation
Broken Access ControlDeny by default, enforce ownership
Cryptographic FailuresHTTPS everywhere, encrypt at rest
Insecure DesignThreat modeling, security requirements
Security MisconfigurationMinimal platforms, remove defaults
Vulnerable ComponentsDependency scanning, auto-updates
Auth FailuresMFA, strong passwords, session limits
Software IntegrityVerify packages, signed commits
Logging FailuresLog all auth events, monitor anomalies
SSRFWhitelist URLs, disable unnecessary protocols

Secure Communication

HTTPS Everywhere

  • Redirect HTTP to HTTPS
  • Use HSTS headers
  • Keep TLS certificates up to date

API Security

  • Rate limiting (prevent brute force)
  • API versioning (graceful deprecation)
  • Request signing (verify integrity)

Logging & Monitoring

What to Log

  • Authentication attempts (success and failure)
  • Authorization failures
  • Input validation errors
  • Unusual traffic patterns

What NOT to Log

  • Passwords
  • API keys
  • Personal health information
  • Credit card numbers

Security Checklist

  • Authentication uses MFA where required
  • Authorization checks resource ownership
  • All inputs validated and sanitized
  • Secrets stored in a secrets manager
  • Dependencies scanned for vulnerabilities
  • HTTPS enforced for all traffic
  • Security headers configured (CSP, HSTS)
  • Rate limiting enabled on public APIs
  • Sensitive data encrypted at rest
  • Security events logged and monitored

Troubleshooting

  • Authentication bypass in tests: ensure test users cannot reach production endpoints.
  • False positives in scanning tools: tune rules against the risk profile. Distinguish between reachable vulnerabilities and theoretical issues.
  • Secrets appear in logs: configure log filters to redact tokens, passwords, and keys. Audit log sinks for sensitive patterns.
  • CSP breaks legitimate functionality: use report-only mode first, then enforce. Iterate on allowed sources based on real violations.
  • Incident response stalls: run tabletop exercises.

Key Takeaways

  • Apply security best practices guide when you need a practical solution for your use case.
  • Monitor performance after implementation; measure latency, errors, and resource usage before and after.
  • Check the Troubleshooting section for common failures; most have documented root causes with fixes.
  • Keep dependencies updated and run tests in CI to prevent production regressions.

Advanced Topics

Scenario: Hardening Node.js API for Production

// 1. Helmet: HTTP security headers
const helmet = require("helmet");
app.use(helmet());
// X-Content-Type-Options: nosniff
// X-Frame-Options: DENY
// Strict-Transport-Security: max-age=31536000
// Content-Security-Policy: default-src self

// 2. Rate limiting
const rateLimit = require("express-rate-limit");
app.use("/api", rateLimit({
  windowMs: 60 * 1000,  // 1 minute
  max: 100,              // 100 requests per minute
  message: "Too many requests"
}));

// 3. Strict CORS
const cors = require("cors");
app.use(cors({
  origin: ["https://app.example.com"],
  methods: ["GET", "POST", "PUT", "DELETE"],
  allowedHeaders: ["Content-Type", "Authorization"],
  credentials: true
}));

// 4. Input validation (Zod)
const { z } = require("zod");
const userSchema = z.object({
  email: z.string().email().max(255),
  password: z.string().min(12).max(128),
  name: z.string().min(1).max(100).regex(/^[a-zA-Z0-9 ]+$/)
});
app.post("/api/users", (req, res) => {
  const result = userSchema.safeParse(req.body);
  if (!result.success) {
    return res.status(400).json({ error: "Invalid input" });
  }
  // ... process
});

// 5. SQL injection prevention (parameterized queries)
app.get("/api/users/:id", async (req, res) => {
  // NEVER: `SELECT * FROM users WHERE id = ${req.params.id}`
  // ALWAYS: parameterized queries
  const result = await pool.query(
    "SELECT id, email, name FROM users WHERE id = $1",
    [req.params.id]
  );
  res.json(result.rows[0]);
});

// 6. Secure JWT
const jwt = require("jsonwebtoken");
const token = jwt.sign(
  { userId: user.id, role: user.role },
  process.env.JWT_SECRET,
  { expiresIn: "15m", algorithm: "RS256" }
);

// 7. Audit logging
app.use((req, res, next) => {
  const start = Date.now();
  res.on("finish", () => {
    logger.info({
      method: req.method,
      path: req.path,
      status: res.statusCode,
      duration: Date.now() - start,
      ip: req.ip,
      userId: req.user?.id,
    });
  });
  next();
});

// 8. Dependency scanning in CI
// npm audit --audit-level=high
// npx snyk test
// npx trivy fs .

Which security headers are mandatory?

X-Content-Type-Options: nosniff (prevent MIME sniffing), Strict-Transport-Security: max-age=31536000 (enforce HTTPS), X-Frame-Options: DENY (prevent clickjacking), Content-Security-Policy: default-src self (prevent XSS), Referrer-Policy: no-referrer (minimize exposed info). Use helmet() in Express to configure all of them automatically.

Common Production Pitfalls

  • Treating the guide as a checklist to complete once rather than a practice to evolve.
  • Adopting every recommendation at once instead of starting with one measured change.
  • Skipping the maturity assessment and forcing advanced practices on an unprepared team.
  • Not updating runbooks and on-call expectations as new practices are introduced.
  • Ignoring real incident data when prioritizing which parts of the guide to apply first.
  • Failing to assign an owner who reviews decisions quarterly.
  • Copying examples without adapting them to the team’s actual tooling and constraints.
  • Forgetting to measure outcomes before adding the next improvement.

Frequently Asked Questions

How often should I update dependencies?

At least monthly. Enable Dependabot or Renovate for automated PRs.

Is JWT secure?

JWT is secure when implemented correctly: short expiry, strong signing algorithms (RS256/ES256), secure secret storage, and HTTPS-only transmission.

Should I encrypt everything in the database?

Encrypt sensitive fields (PII, credentials, tokens). At-rest encryption should be enabled at the database level.