C
Security/Encryption Authentication/Lesson 03

Cryptography + Authentication — Hashing · HTTPS · Session/JWT · Cookies

45 min·theory

Cryptography + Authentication — Hashing · HTTPS · Session/JWT · Cookies

🎯 After reading this lesson

After finishing this lesson, you will be able to confidently handle the following three topics.

  • ✅ bcrypt vs argon2 (MD5/SHA-1 are forbidden)
  • ✅ JWT signing algorithms (HS256 vs RS256)
  • ✅ The four OAuth 2.0 flows (Code · PKCE · Client Credentials · Refresh)

Keep the learning objectives as a checklist — close the lesson only when you can answer all of them.

Password Hashing — bcrypt · argon2 (Never Plaintext)

One-liner: Passwords are stored as hashes (non-reversible). A unique salt is added every time.

Choosing a hash algorithm:

AlgorithmRecommended?Notes
bcryptDe facto standard. cost factor 10–12
argon2✅✅2015 PHC winner. Accounts for memory cost too
scryptMemory cost. Less standardized
PBKDF2Outdated and weak (NIST-only recommendation)
MD5·SHA1❌❌Retire immediately. GPUs can attempt billions per second
SHA-256 (standalone)Fast = easy brute force

bcrypt example (Spring):

java
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(12);
String hash = encoder.encode(plainPassword);       // When saving
boolean ok = encoder.matches(plain, storedHash);   // When verifying

Hashing pitfalls:

  • ❌ MD5(password) → instantly reversible via rainbow tables
  • ❌ SHA256(password) → crackable with a GPU in hours
  • ❌ No salt → identical passwords produce identical hashes
  • ❌ Password length limit (8 chars) — prefer long passwords (16+ char passphrase)
  • ✅ bcrypt + cost 12 + automatic salt (handled by the library)

HTTPS · TLS 1.3 — Encrypting Communication

Why it matters: Plain HTTP on a shared Wi-Fi network means packets can be intercepted — exposing passwords and session tokens.

TLS 1.3 handshake (1-RTT, standard since 2018):
1. Client Hello — list of supported cipher algorithms + start of key exchange
2. Server Hello + Certificate — chosen algorithm + server public-key certificate
3. Verification — client validates the certificate chain (CA → intermediate → server)
4. Session key derivation — shared secret generated via Diffie-Hellman
5. Encrypted communication begins — AES-GCM · ChaCha20

TLS 1.2 vs 1.3 comparison:

ItemTLS 1.2TLS 1.3
Handshake2-RTT1-RTT
Forward SecrecyOptionalMandatory
Weak ciphersManyAll removed
0-RTT reconnectionNoSupported (PSK)

TLS pitfalls:

  • ❌ Let's Encrypt certificate expiry (90 days) — auto-renewal is essential
  • ❌ TLS 1.0/1.1 still active — deprecated in 2020
  • ❌ Mixed Content (HTTP resources on an HTTPS page) — blocked by browsers
  • ✅ Enforce HTTPS with HSTS header: Strict-Transport-Security: max-age=31536000

Authentication — Session vs JWT vs OAuth

Login flow (session-based, most common):
1. User: POST /login (email + password)
2. Server: bcrypt.matches → success
3. Server: generate sessionId + store in Redis/DB
4. Response: Set-Cookie: sessionId=abc; HttpOnly; Secure; SameSite=Lax
5. Subsequent requests: browser automatically attaches the cookie
6. Server: sessionId → look up in Redis → authenticate user

Session vs JWT comparison:

ItemSession (Cookie)JWT (Bearer Token)
Storage locationServer (Redis/DB)Client (LocalStorage · Cookie)
RevocationDelete on server → immediateValid until expiry (blacklist required)
SizesessionId ~32 charsJWT 200–500 chars
ScalabilityRequires RedisStateless
SecurityHttpOnly cookie = XSS-safeLocalStorage = XSS risk
MobileCookies are cumbersomeAuthorization header is clean

Cookie security settings (mandatory):

code
Set-Cookie: sessionId=abc;
  HttpOnly;          // JS access X (XSS defense)
  Secure;            // Transmit only over HTTPS
  SameSite=Lax;      // CSRF defense (no cookie from external sites)
  Max-Age=86400;     // 1 day
  Path=/;

OAuth 2.0 — delegating authentication to another service (Sign in with Google/Kakao):
1. User clicks "Sign in with Google"
2. Google authentication page → user consent
3. Google sends an authorization code to our server
4. Our server: exchange code → access_token (server-to-server)
5. Call Google API with the token (fetch user email and name)
6. Create or log in the user in our DB

> 💡 PKCE is strongly recommended (mobile · SPA): safe even if the code is intercepted by a man-in-the-middle.

💻 📌 Security Headers + Rate Limiting
// === Node.js Express — Security Headers + Rate Limit ===
const express = require('express');
const helmet = require('helmet');
const rateLimit = require('express-rate-limit');

const app = express();

// 1. Automatic security header configuration (CSP·HSTS·X-Frame·X-XSS etc.)
app.use(helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      scriptSrc: ["'self'", "'unsafe-inline'"],  // Allow React inline script
      styleSrc: ["'self'", "'unsafe-inline'"],
      imgSrc: ["'self'", 'data:', 'https://cdn.example.com'],
    },
  },
  hsts: { maxAge: 31536000, includeSubDomains: true, preload: true },
}));

// 2. Rate Limit — Brute-force defense
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000,    // 15 minute window
  max: 5,                      // Same IP 5 times in 15 minutes
  message: 'Too many login attempts. Please try again after 15 minutes.',
  standardHeaders: true,
  legacyHeaders: false,
});
app.post('/login', loginLimiter, async (req, res) => { /* ... */ });

const apiLimiter = rateLimit({
  windowMs: 60 * 1000,    // 1 minute
  max: 100,               // 100 requests per minute
});
app.use('/api/', apiLimiter);

// === Redis-based Rate Limit (Distributed Environment) ===
// const RedisStore = require('rate-limit-redis');
// const Redis = require('ioredis');
// const client = new Redis();
// const limiter = rateLimit({
//   store: new RedisStore({ sendCommand: (...args) => client.call(...args) }),
//   windowMs: 60_000, max: 100,
// });

// === Cookie·Session Security ===
const session = require('express-session');
const RedisStore2 = require('connect-redis').default;
app.use(session({
  store: new RedisStore2({ client: new Redis() }),
  secret: process.env.SESSION_SECRET,
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,        // XSS defense
    secure: true,          // Only over HTTPS
    sameSite: 'lax',       // CSRF defense
    maxAge: 24 * 60 * 60 * 1000,
  },
}));

🤖 Try asking AI like this

Knowing the concepts in this lesson lets you give AI specific instructions — not a vague "fix it," but a vocabulary-driven request that is the starting point for saving tokens.

  • "Migrate this password hashing from MD5 to bcrypt for me"
  • "Walk me through the steps to switch the JWT secret from HS256 to RS256 (asymmetric)"

Why this reduces tokens

Without the concepts, even after receiving an AI response you have to ask "what does that mean?" all over again. That follow-up question is what eats up tokens. Learn the concept once and the conversation ends in a single exchange.

Encryption + Authentication — Hash·HTTPS·Session·JWT - Security