Skip to main content

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

Track Integration

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

PhaseLevelFocusSkills Developed
Phase 4Level 1Secure coding basicsMemory safety, input validation, secure coding practices
Phase 5Level 1Network security fundamentalsTransport security, authentication basics, attack surface awareness
Phase 6-7Level 2Data and API securitySecrets management, access control, threat modeling, API security
Phase 8Level 2Architecture securitySecurity reviews, risk assessment, reliability/security tradeoffs
Phase 9-10Level 3Production securityCloud 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:

  1. 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/
  2. 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
  3. 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:

  1. Input Handling:

    • Are all inputs validated before use?
    • Are buffer sizes checked to prevent overflows?
    • Are special characters and edge cases handled safely?
  2. 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?
  3. 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?
  4. 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:

  1. What could go wrong? (Threat identification)
  2. Who might want to attack this? (Adversary modeling)
  3. What data needs protection? (Asset identification)
  4. How can we detect problems? (Monitoring and incident response)
  5. 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.