Cross-language secure coding practices covering OWASP Top 10, input validation, authentication, cryptography, and supply chain security. Provides concrete code examples in Python, PHP, JavaScript/React, Java, and Go.
Python (Flask):
from functools import wraps
from flask import abort, session
def require_role(role):
def decorator(f):
@wraps(f)
def wrapped(args, *kwargs):
if session.get("role") != role:
abort(403)
return f(args, *kwargs)
return wrapped
return decorator
@app.route("/admin")
@require_role("admin")
def admin_panel():
return "Admin area"
PHP (Laravel):
class CheckRole {
public function handle($request, Closure $next, $role) {
if ($request->user()->role !== $role) abort(403);
return $next($request);
}
}
// Route::get('/admin', [AdminController::class, 'panel'])->middleware('role:admin');
JS (Express):
function requireRole(role) {
return (req, res, next) => {
if (req.user?.role !== role) return res.sendStatus(403);
next();
};
}
app.get("/admin", requireRole("admin"), (req, res) => res.send("Admin area"));
Go:
func RequireRole(role string) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if sess, _ := store.Get(r, "session"); sess.Values["role"] != role {
http.Error(w, "Forbidden", 403); return
}
next.ServeHTTP(w, r)
})
}
}
SQL Injection — Parameterized Queries:
# Python SAFE ✅
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) # parameterized
Python UNSAFE ❌
cursor.execute(f"SELECT * FROM users WHERE id = {user_id}")
// PHP SAFE ✅
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
// PHP UNSAFE ❌
$pdo->query("SELECT * FROM users WHERE id = " . $userId);
// JS SAFE ✅
await pool.query("SELECT * FROM users WHERE id = $1", [userId]);
// JS UNSAFE ❌
await pool.query(SELECT * FROM users WHERE id = ${userId});
// Go SAFE ✅
db.QueryRow("SELECT * FROM users WHERE id = $1", userID)
// Go UNSAFE ❌
db.QueryRow(fmt.Sprintf("SELECT * FROM users WHERE id = %s", userID))
XSS — Output Encoding:
// React — auto-encodes in JSX ✅
return {userInput};
// React — DANGEROUS ❌
return ;
// PHP — SAFE
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
Command Injection:
# SAFE ✅ — list form, no shell
subprocess.run(["ls", "-l", user_dir])
UNSAFE ❌
subprocess.run(f"ls -l {user_dir}", shell=True)
import structlog
logger = structlog.get_logger()
logger.warning("login_failed", ip=request.remote_addr, username=username, count=attempts)
import re
ALLOWED_USERNAME = re.compile(r"^[a-zA-Z0-9_-]{3,32}$")
if not ALLOWED_USERNAME.match(username):
raise ValueError("Invalid username")
const userSchema = z.object({
username: z.string().regex(/^[a-zA-Z0-9_-]{3,32}$/),
email: z.string().email().max(254),
age: z.number().int().min(0).max(150),
});
const parsed = userSchema.parse(input);
type User struct {
Username string validate:"required,alphanum,min=3,max=32"
Email string validate:"required,email"
}
validate := validator.New()
if err := validate.Struct(user); err != nil { / reject / }
| Context | Method | Example |
|---------|--------|---------|
| HTML body | Entity encode | htmlspecialchars($val, ENT_QUOTES, 'UTF-8') |
| HTML attribute | Attribute encode | encodeURIComponent() |
| JavaScript | JSON encode | json_encode($val, JSON_HEX_TAG) |
| URL | URL encode | rawurlencode($val) |
| SQL | Parameterize | Never concatenate |
| CSS | CSS escape | CSS.escape() |
random / Math.random for Securityimport secrets
token = secrets.token_urlsafe(32) # NOT random module
$token = bin2hex(random_bytes(32)); // NOT rand()/mt_rand()
const a = new Uint32Array(8);
crypto.getRandomValues(a); // NOT Math.random()
b := make([]byte, 32)
crypto_rand.Read(b) // NOT math/rand
sequenceDiagram
participant U as User Agent
participant C as Client App
participant A as Auth Server / IdP
participant R as Resource Server
U->>C: Click "Login"
C->>A: GET /authorize?response_type=code&client_id=...&code_challenge=S256
A->>U: Login + Consent page
U->>A: Submit credentials + consent
A->>C: 302 redirect_uri?code=AUTH_CODE&state=CSRF_TOKEN
C->>A: POST /token {grant_type=authorization_code, code, code_verifier}
A->>C: {access_token, id_token, refresh_token}
C->>R: GET /resource (Authorization: Bearer access_token)
R->>C: Protected resource
C->>U: Display data
# Python — PyJWT with RS256
import jwt
token = jwt.encode(
{"sub": user_id, "exp": now + timedelta(minutes=15), "iss": "myapp"},
private_key, algorithm="RS256",
)
payload = jwt.decode(token, public_key, algorithms=["RS256"], issuer="myapp", audience="myapp-api")
// JS — jose library
import { jwtVerify, SignJWT } from "jose";
const token = await new SignJWT({ sub: userId })
.setProtectedHeader({ alg: "RS256" })
.setExpirationTime("15m")
.setIssuer("myapp")
.sign(privateKey);
const { payload } = await jwtVerify(token, publicKey, { algorithms: ["RS256"], issuer: "myapp" });
JWT Pitfalls: Never alg=none; prefer RS256/ES256 over HMAC; keep TTL ≤15 min; always verify iss, aud, exp.
# RBAC
PERMISSIONS = {"admin": ["read","write","delete"], "editor": ["read","write"], "viewer": ["read"]}
def has_permission(role, action): return action in PERMISSIONS.get(role, [])
ABAC — Open Policy Agent (policy.rego)
allow { input.user.role == "editor"; input.resource.owner == input.user.id; input.action == "write" }
// Go — Casbin
enforcer, _ := casbin.NewEnforcer("model.conf", "policy.csv")
ok := enforcer.Enforce(user.Role, resource, action)
import pyotp
secret = pyotp.random_base32() # store encrypted
uri = pyotp.totp.TOTP(secret).provisioning_uri(user.email, issuer="MyApp")
totp = pyotp.TOTP(secret)
if not totp.verify(user_code, valid_window=1): raise AuthError("Invalid MFA")
# Python — Flask secure session
app.config.update(
SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SECURE=True,
SESSION_COOKIE_SAMESITE="Lax", PERMANENT_SESSION_LIFETIME=timedelta(minutes=30),
)
// PHP
ini_set('session.cookie_httponly', '1');
ini_set('session.cookie_secure', '1');
ini_set('session.cookie_samesite', 'Lax');
session_regenerate_id(true); // after login, delete old session
# Python
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
key = AESGCM.generate_key(bit_length=256)
aesgcm = AESGCM(key)
nonce = os.urandom(12) # 96-bit, NEVER reuse with same key
ct = aesgcm.encrypt(nonce, plaintext, associated_data)
// JS — Web Crypto API
const key = await crypto.subtle.generateKey({ name: "AES-GCM", length: 256 }, true, ["encrypt","decrypt"]);
const iv = crypto.getRandomValues(new Uint8Array(12));
const ct = await crypto.subtle.encrypt({ name: "AES-GCM", iv }, key, data);
// Go
block, _ := aes.NewCipher(key)
gcm, _ := cipher.NewGCM(block)
nonce := make([]byte, gcm.NonceSize())
io.ReadFull(rand.Reader, nonce)
ct := gcm.Seal(nil, nonce, plaintext, nil)
from cryptography.hazmat.primitives.asymmetric import padding
ciphertext = public_key.encrypt(plaintext,
padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None))
flowchart TD
A[Schedule Rotation - 90 days] --> B{Key due?}
B -->|Yes| C[Generate new key pair]
C --> D[Store new key v_n+1 in Vault/KMS]
D --> E[App config: ENCRYPT with n+1]
E --> F[Keep key v_n for DECRYPTION only]
F --> G[Batch re-encrypt data at rest with n+1]
G --> H[Audit re-encryption complete]
H --> I[Mark key v_n deprecated]
I --> J[Grace period → destroy key v_n]
J --> K[Log rotation complete]
B -->|No| L[Continue current key]
style C fill:#2d6,stroke:#000
style J fill:#d22,stroke:#000
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.minimum_version = ssl.TLSVersion.TLSv1_3
ctx.verify_mode = ssl.CERT_REQUIRED
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS13, CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}}
import hvac
client = hvac.Client(url="https://vault:8200")
client.auth.approle.login(role_id=ROLE_ID, secret_id=SECRET_ID)
secret = client.secrets.kv.v2.read_secret_version(path="myapp/db")
db_password = secret["data"]["data"]["password"] # lives in memory only
const vault = require("node-vault")({ endpoint: "https://vault:8200" });
await vault.approleLogin({ role_id: ROLE_ID, secret_id: SECRET_ID });
const { data } = await vault.read("secret/data/myapp/db");
# Syft — language-agnostic
syft dir:./ -o spdx-json > sbom.spdx.json
syft dir:./ -o cyclonedx-json > sbom.cyclonedx.json
Python-specific
pip-audit -r requirements.txt && cyclonedx-py environment -o sbom.json
pip-audit -r requirements.txt # Python
composer audit # PHP
npm audit --audit-level=high # JS
govulncheck ./... # Go
mvn org.owasp:dependency-check:check # Java
| Level | Requirements |
|-------|-------------|
| 1 | Build provenance documented |
| 2 | Hosted build platform, signed builds |
| 3 | Hardened build, non-falsifiable provenance |
| 4 | Hermetic + reproducible builds, two-party review |
cosign sign --key cosign.key myregistry/myapp:v1.2.3 # key-based
cosign sign myregistry/myapp:v1.2.3 # keyless (Fulcio)
cosign verify --key cosign.pub myregistry/myapp:v1.2.3 # verify
# requirements.txt — pin exact versions ✅
django==4.2.11
celery==5.3.6
Generate: pip-compile requirements.in -o requirements.txt
// package.json — exact versions ✅
"dependencies": { "express": "4.18.3" } // NOT ^4.18.3
// Commit package-lock.json; use npm ci for reproducible installs
// go.mod + go.sum — checked into VCS ✅
go mod tidy // updates go.sum
go mod verify // verifies module hashes
flowchart LR
A[Developer commits] --> B[CI triggers]
B --> C[Generate SBOM - Syft]
C --> D[Scan deps - pip-audit/npm-audit/govulncheck]
D --> E{Critical CVEs?}
E -->|Yes| F[Block build + alert]
E -->|No| G[Build artifacts]
G --> H[Sign image - Cosign]
H --> I[Attach SBOM + provenance]
I --> J[Push to registry]
J --> K[Deploy: verify sig + SLSA provenance]
K --> L[Admission controller validates before creation]
style F fill:#d44,stroke:#000
style L fill:#4d4,stroke:#000
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27497364c9af683 # SHA, not tag
- uses: github/codeql-action/analyze@5234436df968eb8925d3e2ba4809353aa0c7573e
| Area | Must-Do |
|------|---------|
| Injection | Parameterize queries; allowlist input; encode output per context |
| Auth | OAuth2 PKCE; short JWTs (≤15m); MFA; regenerate sessions; SameSite cookies |
| Crypto | AES-256-GCM; RSA-OAEP; TLS 1.3; rotate keys; Vault/KMS; CSPRNG only |
| Access | Server-side enforcement; deny default; RBAC/ABAC; log changes |
| Supply Chain | SBOM; CI scanning; pin deps; sign artifacts; verify on deploy |
| Secrets | Never in VCS; Vault/env injection; rotate on compromise |
| Logging | Structured logs; audit auth; no PII; tamper-evident |
| Headers | CSP, HSTS, X-Content-Type-Options, X-Frame-Options, Referrer-Policy |