Track C: Security Engineering Overview
Purpose: Build security awareness and defensive programming habits that protect systems and data from threats
Philosophy: Security is not a feature to add later - it's a design constraint that shapes every engineering decision
This track begins in Phase 4 when systems programming starts and continues through all production-oriented phases. Security thinking becomes part of every architectural and implementation decision.
Track Progression by Phase
| Phase | Level | Focus | Skills Developed |
|---|---|---|---|
| Phase 4 | Level 1 | Secure coding basics | Memory safety, input validation, secure coding practices |
| Phase 5 | Level 1 | Network security fundamentals | Transport security, authentication basics, attack surface awareness |
| Phase 6-7 | Level 2 | Data and API security | Secrets management, access control, threat modeling, API security |
| Phase 8 | Level 2 | Architecture security | Security reviews, risk assessment, reliability/security tradeoffs |
| Phase 9-10 | Level 3 | Production security | Cloud security posture, IAM design, infrastructure hardening, incident response |
Level 1 Starter Guide: Secure Coding Basics
Getting Started Today (60 minutes)
Set up security-aware development practices:
-
Configure secure development environment:
# Install security linting tools
pip install bandit safety
# Check for known vulnerabilities in dependencies
safety check
# Scan code for security issues
bandit -r your_project/ -
Write your first secure function:
import hashlib
import secrets
def secure_password_hash(password: str) -> tuple[str, str]:
"""Hash password with random salt using secure methods."""
# Generate cryptographically secure random salt
salt = secrets.token_hex(32)
# Use strong hashing algorithm with salt
password_hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt.encode('utf-8'),
100000 # Iterations make brute force expensive
)
return password_hash.hex(), salt
def verify_password(password: str, stored_hash: str, salt: str) -> bool:
"""Verify password against stored hash and salt."""
computed_hash = hashlib.pbkdf2_hmac(
'sha256',
password.encode('utf-8'),
salt.encode('utf-8'),
100000
)
return computed_hash.hex() == stored_hash -
Test security properties:
def test_password_security():
# Test that same password produces different hashes (due to salt)
hash1, salt1 = secure_password_hash("test123")
hash2, salt2 = secure_password_hash("test123")
assert hash1 != hash2 # Different salts produce different hashes
assert salt1 != salt2 # Salts are random
# Test verification works correctly
assert verify_password("test123", hash1, salt1) == True
assert verify_password("wrong", hash1, salt1) == False
Core Security Principles (Learn These First)
1. Input Validation and Sanitization
- Never trust user input - validate, sanitize, and constrain all external data
- Principle of least privilege - grant minimum necessary permissions
- Defense in depth - multiple security layers, not single point of failure
2. Secure Defaults
- Fail securely - when errors occur, default to denying access rather than permitting it
- Explicit security decisions - make security choices consciously rather than by accident
- Minimize attack surface - disable unnecessary features, services, and access points
3. Cryptographic Hygiene
- Use proven libraries - don't implement cryptographic primitives yourself
- Secure random generation - use cryptographically secure randomness for keys, tokens, salts
- Proper key management - store, rotate, and revoke keys following established practices
Your First Security Checklist
For every implementation, ask:
-
Input Handling:
- Are all inputs validated before use?
- Are buffer sizes checked to prevent overflows?
- Are special characters and edge cases handled safely?
-
Data Protection:
- Are sensitive data (passwords, keys, tokens) handled securely?
- Are temporary files and memory cleaned up appropriately?
- Are secrets separated from configuration and source code?
-
Error Handling:
- Do error messages avoid revealing sensitive information?
- Do errors fail securely rather than exposing system internals?
- Are exceptions and edge cases handled without creating security vulnerabilities?
-
Access Control:
- Are permissions checked before performing sensitive operations?
- Are authentication and authorization handled correctly?
- Are administrative functions protected appropriately?
Practical Security Techniques by Content Area
Mathematical Implementations (Phase 4)
Secure random number generation:
import secrets
# Cryptographically secure randomness
def generate_secure_key(length: int) -> bytes:
return secrets.token_bytes(length)
def secure_random_choice(sequence):
return secrets.choice(sequence)
# Test randomness properties
def test_randomness_quality():
# Generate many samples and test for bias
samples = [secrets.randbelow(6) for _ in range(10000)]
for i in range(6):
count = samples.count(i)
# Should be roughly 1/6 of samples (allow statistical variation)
assert 1500 < count < 1850
Numerical stability and precision:
import decimal
def secure_financial_calculation(amount: decimal.Decimal, rate: decimal.Decimal) -> decimal.Decimal:
"""Use decimal arithmetic for financial calculations to prevent precision attacks."""
# Validate inputs
if amount < 0 or rate < 0 or rate > 1:
raise ValueError("Invalid financial parameters")
# Use fixed-point arithmetic
return amount * (decimal.Decimal('1') + rate)
Systems Programming Security (Phase 5)
Memory safety in C:
// Secure string handling
#include <string.h>
#include <stdlib.h>
char* safe_string_copy(const char* source, size_t max_length) {
if (!source || max_length == 0) {
return NULL;
}
// Allocate with null terminator space
char* dest = malloc(max_length + 1);
if (!dest) {
return NULL; // Handle allocation failure
}
// Use safe string copy with length limit
strncpy(dest, source, max_length);
dest[max_length] = '\0'; // Ensure null termination
return dest;
}
void test_string_safety() {
char* result = safe_string_copy("hello world", 5);
assert(result != NULL);
assert(strlen(result) == 5);
assert(strcmp(result, "hello") == 0);
free(result);
}
Network programming security:
// Secure socket programming with input validation
#include <sys/socket.h>
#include <netinet/in.h>
#define MAX_BUFFER_SIZE 1024
ssize_t secure_recv(int socket, char* buffer, size_t buffer_size) {
// Validate parameters
if (!buffer || buffer_size == 0 || buffer_size > MAX_BUFFER_SIZE) {
return -1;
}
// Receive with size limit
ssize_t bytes_received = recv(socket, buffer, buffer_size - 1, 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0'; // Null terminate
}
return bytes_received;
}
Service Security (Phases 6-8)
API input validation:
from pydantic import BaseModel, validator
from typing import Optional
import re
class UserRegistration(BaseModel):
username: str
email: str
password: str
@validator('username')
def validate_username(cls, v):
if not re.match(r'^[a-zA-Z0-9_-]+$', v):
raise ValueError('Username contains invalid characters')
if len(v) < 3 or len(v) > 50:
raise ValueError('Username must be 3-50 characters')
return v
@validator('email')
def validate_email(cls, v):
if not re.match(r'^[^@]+@[^@]+\.[^@]+$', v):
raise ValueError('Invalid email format')
return v.lower()
@validator('password')
def validate_password(cls, v):
if len(v) < 12:
raise ValueError('Password must be at least 12 characters')
return v
Authentication and authorization:
import jwt
from datetime import datetime, timedelta
def create_secure_token(user_id: str, secret_key: str) -> str:
"""Create JWT token with expiration and secure claims."""
payload = {
'user_id': user_id,
'exp': datetime.utcnow() + timedelta(hours=24),
'iat': datetime.utcnow(),
'iss': 'your-service-name'
}
return jwt.encode(payload, secret_key, algorithm='HS256')
def verify_token(token: str, secret_key: str) -> Optional[dict]:
"""Verify JWT token and return claims if valid."""
try:
payload = jwt.decode(token, secret_key, algorithms=['HS256'])
return payload
except jwt.ExpiredSignatureError:
return None # Token expired
except jwt.InvalidTokenError:
return None # Invalid token
Security Resources by Phase
Phase 4: Secure Coding Fundamentals
- OWASP Top 10 - Most critical web application security risks
- The Art of Software Security Assessment - Code review for security vulnerabilities
- CERT Secure Coding Standards - Language-specific secure programming practices
Phase 5: Network and Systems Security
- Network Security Essentials - Cryptography, network protocols, and security mechanisms
- Missing Semester Lecture 9: Security and cryptography fundamentals
- Hacking: The Art of Exploitation - Understanding attack techniques to build better defenses
Phase 6-8: Application and Service Security
- Web Application Security - Comprehensive guide to web security
- OWASP Application Security Verification Standard - Systematic approach to application security requirements
- Threat Modeling: Designing for Security - Systematic approach to identifying and mitigating security risks
Phase 9-10: Infrastructure and Cloud Security
- Cloud Security and Privacy - Cloud-specific security challenges and solutions
- NIST Cybersecurity Framework - Systematic approach to organizational security
- Site Reliability Engineering - Security considerations in production operations
Security Assessment and Verification
Phase-Appropriate Security Assessments
Phase 4-5: Code Security Review
- Manual code review for common vulnerabilities (buffer overflows, injection flaws, race conditions)
- Automated security scanning with tools like Bandit (Python), Clang Static Analyzer (C)
- Peer security review focusing on input validation and error handling
Phase 6-8: Application Security Testing
- API security testing for authentication, authorization, and input validation
- Database security review for injection prevention and access control
- Threat modeling for service architectures and data flow analysis
Phase 9-10: Infrastructure Security Assessment
- Infrastructure security scanning for misconfigurations and vulnerabilities
- Penetration testing and red team exercises for production systems
- Compliance assessment and security audit preparation
Building Security Mindset
Security questions for every design decision:
- What could go wrong? (Threat identification)
- Who might want to attack this? (Adversary modeling)
- What data needs protection? (Asset identification)
- How can we detect problems? (Monitoring and incident response)
- How do we recover from compromise? (Business continuity and disaster recovery)
Security integration with other tracks:
- Testing Track: Security test cases and penetration testing
- Git/CI-CD Track: Secure development workflow and supply chain security
- Observability Track: Security monitoring and incident detection
This security track builds defensive thinking and practical security engineering skills essential for building trustworthy systems in production environments.