02 Reviewing Authentication and Authorization
Authentication (AuthN) and Authorization (AuthZ) are the most critical components of any application. Flaws here often lead to complete system compromise.
1. Broken Authentication: Weak Password Hashing
Context: Storing passwords securely is fundamental. MD5, SHA1, and even standard SHA256 are too fast and vulnerable to brute-force and dictionary attacks.
Vulnerable Pattern (Python/Flask)
import hashlib
from flask import request
@app.route('/register', methods=['POST'])
def register():
password = request.form['password']
# BAD: Using SHA-256 (too fast, no salt by default)
hashed = hashlib.sha256(password.encode()).hexdigest()
db.execute("INSERT INTO users (password) VALUES (?)", (hashed,))
Secure Pattern (Python/Flask)
import bcrypt
from flask import request
@app.route('/register', methods=['POST'])
def register():
password = request.form['password'].encode('utf-8')
# GOOD: Using bcrypt with a work factor
salt = bcrypt.gensalt(rounds=12)
hashed = bcrypt.hashpw(password, salt)
db.execute("INSERT INTO users (password) VALUES (?)", (hashed,))
2. JWT Verification Bugs
Context: JSON Web Tokens (JWT) are widely used for stateless authentication. Common flaws include not verifying the signature or trusting the alg header blindly.
Vulnerable Pattern (Node.js)
const jwt = require('jsonwebtoken');
function verifyToken(req, res, next) {
const token = req.headers['authorization'];
// BAD: Decoding without verifying the signature
const decoded = jwt.decode(token);
if (decoded) {
req.user = decoded;
next();
} else {
res.status(401).send();
}
}
Secure Pattern (Node.js)
const jwt = require('jsonwebtoken');
const SECRET = process.env.JWT_SECRET;
function verifyToken(req, res, next) {
const token = req.headers['authorization'];
try {
// GOOD: Verifying the signature and explicitly defining allowed algorithms
const decoded = jwt.verify(token, SECRET, { algorithms: ['HS256'] });
req.user = decoded;
next();
} catch (err) {
res.status(401).send();
}
}
3. Broken Object Level Authorization (BOLA / IDOR)
Context: BOLA occurs when an application exposes a reference to an internal implementation object but fails to check if the current user has permission to access that specific object.
Vulnerable Pattern (Go)
func GetReceipt(w http.ResponseWriter, r *http.Request) {
// Extract receipt ID from URL (e.g., /receipts/123)
vars := mux.Vars(r)
receiptID := vars["id"]
// BAD: Fetching the receipt without checking who owns it
receipt, err := db.GetReceiptByID(receiptID)
if err != nil {
http.Error(w, "Not found", 404)
return
}
json.NewEncoder(w).Encode(receipt)
}
Secure Pattern (Go)
func GetReceipt(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
receiptID := vars["id"]
// Get the authenticated user ID from context
userID := r.Context().Value("userID").(string)
receipt, err := db.GetReceiptByID(receiptID)
if err != nil {
http.Error(w, "Not found", 404)
return
}
// GOOD: Verify ownership before returning data
if receipt.OwnerID != userID {
http.Error(w, "Forbidden", 403)
return
}
json.NewEncoder(w).Encode(receipt)
}