1.
How do secure coding principles and vulnerability classifications like CWE help prevent software
weaknesses during development?
Secure coding principles and vulnerability classifications such as CWE (Common Weakness Enumeration) play a fundamental
role in preventing software weaknesses before they appear in deployed systems. Together, they provide developers with both a
mindset and a structured reference system for identifying, understanding, and avoiding security flaws during software
development.
1. Secure Coding Principles — Building Security into the Development Process
Secure coding principles are guidelines that ensure code is written to resist both accidental failures and malicious attacks. Safe
programming: writing code that minimizes errors, avoids vulnerabilities, and behaves reliably even under unexpected or hostile
conditions.
By following secure coding principles, developers embed security at every step — from design to implementation — rather than
trying to “patch” vulnerabilities later. Key secure coding principles include:
Input Validation and Sanitization:
Every external input must be verified to ensure it’s in the expected format and range. For example, using prepared
statements in database queries prevents SQL injection attacks, as shown in the “Little Bobby Tables” example.
Error Handling and Fail-Safe Defaults:
Code should handle exceptions gracefully without revealing sensitive internal information (like stack traces or SQL
queries). Systems should default to a secure state when an error occurs — for example, denying access rather than
granting it.
Least Privilege and Access Control:
Programs, users, and components should run with the minimal privileges necessary. This limits damage if a component
is compromised.
Memory Safety:
Avoid unsafe operations that can cause buffer overflows or memory corruption. Techniques like bounds-checking,
using safe library functions, or adopting memory-safe languages (Rust, Java) mitigate this class of vulnerabilities.
Use of Trusted Components and Regular Updates:
Vulnerabilities often come from outdated or insecure third-party libraries. Secure coding practices emphasize
dependency management and patching (e.g., avoiding the kind of unpatched library that led to the Equifax breach).
These principles directly influence design decisions. For instance, a developer who understands “security by design” will never
log passwords or sensitive user data, will encrypt communications (TLS), and will architect systems to limit the blast radius of
potential attacks.
Secure coding also ensures compliance with industry standards such as OWASP Top 10, PCI DSS, HIPAA, and GDPR, all of
which translate these principles into concrete coding requirements — e.g., “Encrypt personal data at rest and in transit” or
“Validate all input from untrusted sources.”
In short, secure coding principles serve as proactive prevention: they stop vulnerabilities from entering the codebase in the first
place.
2. Role of Vulnerability Classifications like CWE
While secure coding provides general rules, vulnerability classifications like the Common Weakness Enumeration (CWE)
offer developers a catalogue of specific, well-documented software weaknesses that have been observed in real systems.
CWE is maintained by MITRE and acts as a dictionary of common software errors and their causes. Each CWE entry represents
a recurring coding mistake (for example, CWE-79 – Cross-Site Scripting, CWE-89 – SQL Injection, CWE-120 – Buffer
Overflow, CWE-259 – Hardcoded Passwords).
Purpose and Benefits of CWE:
1. Education and Awareness:
Developers can learn from historical security failures. By studying CWE examples, they understand how certain coding
patterns lead to exploitable weaknesses.
2. Early Detection through Standardization:
CWE provides a common language for describing vulnerabilities. This allows static analysis tools, code review
checklists, and testing frameworks to automatically detect known weakness patterns in the source code.
3. Integration into Development Lifecycle:
During the planning and design phase, CWE helps teams identify which weaknesses are most relevant to their
technology stack (for example, memory safety issues in C/C++, injection in web apps).
During implementation, developers can reference CWE entries to verify they are following correct mitigation
techniques — for instance, using proper input validation functions to prevent CWE-89 (SQL Injection).
4. Alignment with Standards:
CWE underpins frameworks like OWASP Top 10 and CERT Secure Coding Standards, ensuring consistent
terminology across the industry. This helps organizations benchmark their code quality and perform vulnerability
assessments systematically.
5. Continuous Improvement and Measurement:
Organizations can track how many CWE-type weaknesses their codebase contains and measure progress over time.
This makes security an objective metric, not a vague goal.
2. What common insecure coding habits expose software to risk, and how can proper
reviews and expectation handling prevent them?
Software security is often undermined not by complex attacks but by developers’ own insecure coding habits. Lecture 2 identifies
a range of bad practices that weaken safety, reliability, and security — many of which arise from time pressure, lack of
discipline, or poor development culture.
Recognizing and correcting these habits through structured code reviews and robust error/expectation handling is crucial for
preventing software failures and vulnerabilities before deployment.
1. Common Insecure Coding Habits That Expose Software to Risk
a) Copy-Paste Coding (Code Duplication)
Description: Developers often copy code fragments from one part of a project (or even from the Internet) and paste them
elsewhere instead of reusing or refactoring.
Risk:
Creates multiple different copies of the same logic.
If a security flaw or bug exists in one instance, it likely exists in all copies.
Fixing one copy but forgetting others leaves exploitable gaps.
Example: Duplicating input validation routines—if one version forgets to sanitize user input, it becomes a vector for
SQL Injection or XSS.
Best Practice: Follow the DRY (Don’t Repeat Yourself) principle — create reusable functions or modules and
centralize logic.
How Reviews Help: Code reviewers can detect duplication and enforce refactoring before merge, reducing attack
surface and maintenance burden.
b) Weak or Ignored Error Handling
Description: Writing empty catch blocks or ignoring exceptions altogether.
Risk:
System continues running in an unstable or insecure state.
Critical failures or attacks go unnoticed because no logs are created.
Attackers can exploit unhandled exceptions to crash systems (DoS) or leak error information.
Example:
try {
int result = 10 / 0;
} catch (Exception e) {
// do nothing 🚨
}
This silently ignores a runtime error, producing undefined behavior.
Best Practice: Implement fail-safe defaults — handle exceptions gracefully, log technical details internally, and show generic
messages externally.
How Reviews Help: Peer review ensures that every exception is handled meaningfully, and no “do-nothing” catch blocks slip
into production.
c) Hardcoding Credentials or Secrets
Description: Embedding usernames, passwords, API keys, or tokens directly in source code.
Risk:
Exposes sensitive data if code is leaked or decompiled.
Violates standards like OWASP and PCI DSS.
Makes credential rotation or revocation extremely difficult.
Example:
String username = "admin";
String password = "12345";
Real Impact: Mirai botnet (2016) exploited default hardcoded passwords in IoT devices to build massive botnets.
Best Practice: Store credentials in environment variables or secure vaults (e.g., AWS Secrets Manager, HashiCorp Vault).
How Reviews Help: Static code analysis and peer reviews detect hardcoded strings resembling secrets before commit.
d) Magic Numbers and Unexplained Constants
Description: Using raw numeric values in code without explanation (e.g., if(score >= 75) instead of if(score >=
PASS_MARK)).
Risk:
Confuses future maintainers; prone to logic errors.
Inconsistencies occur when numbers need to change (one updated, others missed).
Best Practice: Replace literals with named constants or enums, improving readability and maintainability.
How Reviews Help: Reviewers flag unexplained literals and request constants, ensuring consistency across the
codebase.
e) Overuse of Global Variables
Description: Using shared mutable state accessible from anywhere.
Risk:
Leads to race conditions and unpredictable behavior.
One part of the program can accidentally overwrite another’s data.
Makes reasoning about security and correctness extremely hard.
Example:
public static int count = 0;
Best Practice: Apply encapsulation — keep variables private and provide controlled access via methods.
How Reviews Help: Reviewers identify global state misuse and suggest modular, object-oriented alternatives.
f) Long, Monolithic Methods
Description: Writing one giant function that does everything (input validation, processing, logging, etc.).
Risk:
Hides vulnerabilities inside long untested blocks.
Difficult to test or reuse securely.
Encourages developers to skip detailed review due to length and complexity.
Best Practice: Apply the Single Responsibility Principle (SRP) — split methods into small, focused functions.
How Reviews Help: Code reviewers enforce modular design and ensure each function is testable, predictable, and
auditable.
g) Lack of Testing and Skipping Code Reviews
Description: Deploying untested or unreviewed code due to time constraints or overconfidence.
Risk:
Undiscovered bugs reach production.
Missed validation or security checks cause vulnerabilities (e.g., unhandled division by zero, SQL injection).
Best Practice:
Write unit tests for critical functions.
Require peer review approval before merging.
How Reviews Help: Structured code reviews detect logical errors, unsafe dependencies, or missing tests that the
original developer overlooked.
h) Over-Engineering
Description: Adding unnecessary layers, abstractions, or features “just in case.”
Risk:
Increases attack surface and complexity.
Wastes time and creates maintenance overhead.
Example: Implementing an unnecessary class hierarchy for a simple arithmetic function.
Best Practice: Follow KISS (Keep It Simple, Stupid) and YAGNI (You Aren’t Gonna Need It) principles — build
only what’s needed.
How Reviews Help: Senior reviewers can challenge unjustified complexity, ensuring simplicity and focus.
i) Hardcoded Configurations and File Paths
Description: Using absolute, environment-specific paths inside code (e.g., C:/Users/Admin/[Link]).
Risk:
Makes the program non-portable and error-prone.
Causes runtime failures when deployed to a different environment.
Best Practice: Externalize configs to environment variables or config files.
How Reviews Help: Code reviewers verify that environment-specific data isn’t hardcoded.
j) Logging Sensitive Data
Description: Recording confidential info such as passwords, personal identifiers, or cryptographic keys in logs.
Risk:
Violates privacy laws (GDPR, HIPAA).
Exposes secrets in plaintext during incident analysis or backups.
Best Practice: Sanitize logs — never record sensitive data, and mask or hash anything confidential.
How Reviews Help: Log statements are audited during review to ensure no sensitive data is printed or stored.
How do threat models and OWASP Top 10 help detect logic flaws, attack surfaces, and validate
application security?
They help developers think like attackers, identify potential logic flaws and exposed attack surfaces early in the Software Development Life
Cycle (SDLC), and validate that security controls are effective before deployment.
Threat modeling is the structured process of discovering how attackers might abuse a system and how defenses should be built in
advance. 1.1. The Threat Modeling Process
The typical stages are:
Identify Assets → Identify Threats → Identify Vulnerabilities → Plan Mitigations.
Assets – anything of value (user data, payment info, credentials).
Threats – what could go wrong (data theft, privilege escalation, denial of service).
Vulnerabilities – design or coding weaknesses that could enable threats.
Mitigations – security controls to prevent or minimize the impact.
This process enforces security by design: developers understand their system’s trust boundaries, data flows, and potential abuse
cases before writing a line of code.
2. Formal Threat Modeling Methodologies
Lecture 3 introduces three complementary techniques that make the process systematic.
2.1. STRIDE (Microsoft Model)
STRIDE categorizes threats into six types:
Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service, Elevation of Privilege.
Each category corresponds to a set of common weaknesses and defenses:
Threat Example Mitigation
Spoofing Attacker impersonates another user Strong authentication
Tampering Altering data in transit Integrity checks, hashing
Repudiation Denying actions without proof Secure logging
Info Disclosure Sensitive data leak Encryption, access controls
DoS Resource exhaustion Rate-limiting, resource quotas
Privilege Elevation Gaining admin access Authorization checks
Using STRIDE during design ensures that every potential attack type is addressed with an explicit defense.
For example, analyzing an e-commerce checkout flow with STRIDE would reveal that failing to authenticate callbacks
(Repudiation) or validate totals (Tampering) could cause major financial losses.
2.3. Attack Trees
Attack trees visualize an attacker’s goal and all possible paths to achieve it.
The root node is the attacker’s objective (e.g., steal customer data), and branches represent sub-goals (e.g., bypass login, exploit
SQL injection, abuse API token).
By analyzing every branch, developers see which steps are realistic and where to place defenses.
For example, a branch like “forge OAuth state token” would have revealed the missing validation flaw in the lab scenario (OAuth
callback missing state).
Attack trees thus bridge abstract design and concrete exploit paths.
5. OWASP Top 10 — Validating Application Security
The OWASP Top 10 is a community-curated list of the most critical web application security risks (A01–A10).
While threat modeling identifies potential weaknesses, OWASP Top 10 provides a practical checklist to validate that known
vulnerability classes are addressed.
OWASP Top 10 Category Design/Logic Risk Detected Related Threat Model Element
A01 – Broken Access Control Missing authorization checks STRIDE: Elevation of Privilege
A02 – Cryptographic Failures Poor data protection design STRIDE: Information Disclosure
A03 – Injection Missing input validation STRIDE: Tampering
A04 – Insecure Design Business logic flaws Attack Trees / PASTA
A05 – Security Misconfiguration Exposed or weak defaults Attack Surface Review
A06 – Vulnerable & Outdated Components Unpatched dependencies Threat Analysis / Risk Prioritization
A09 – Logging & Monitoring Failures No detection of attacks STRIDE: Repudiation
A10 – Server-Side Request Forgery (SSRF) Uncontrolled outbound requests STRIDE: Information Disclosure / DoS
How does memory management affect software security, and what measures like ASLR
help prevent overflows and leaks?
Memory management is one of the most critical areas of software security because most serious vulnerabilities originate from
improper handling of memory — such as buffer overflows, memory leaks, dangling pointers, and use-after-free errors.
The Relationship Between Memory Management and Software Security
Memory management defines how a program allocates, uses, and releases computer memory.
When developers fail to handle memory safely, it can lead to unpredictable behavior, crashes, data corruption, or even
remote code execution.
1.1. Common Memory-Related Vulnerabilities
Vulnerability Description Security Impact
Writing data beyond the allocated buffer’s boundary Can overwrite function return addresses, enabling code injection and
Buffer Overflow
(e.g., array or string). execution of malicious payloads.
Heap Overflow Overwriting data in the heap segment of memory. Attackers manipulate dynamic memory structures to hijack control flow.
Use-After-Free Accessing memory after it has been released. May allow attackers to execute arbitrary code or cause data corruption.
Dangling Keeping references to memory that is no longer
Leads to unpredictable crashes or security bypasses.
Pointers valid.
Memory Leaks Failing to free memory after use. Gradually consumes system memory, causing Denial of Service (DoS).
These vulnerabilities are especially common in low-level languages like C and C++, which allow direct memory manipulation without automatic
safety checks.
How ASLR Prevents Overflows and Memory Exploits
ASLR (Address Space Layout Randomization)
ASLR (Address Space Layout Randomization) is one of the most important memory protection mechanisms. Normally, a
program’s memory layout (stack, heap, libraries, etc.) loads at predictable locations each time it runs.
Attackers can exploit this predictability by overwriting control data (like return addresses) to jump into known memory locations
— for example, shellcode stored in the stack or the address of a library function like system().
With ASLR enabled:
The base addresses of the stack, heap, libraries, and executable are randomized each time the program runs.
This means that even if a buffer overflow occurs, the attacker cannot reliably predict where their payload will
execute.
As a result, exploitation attempts crash instead of succeeding.
4.2. Example
Without ASLR:
Stack: 0x7fffffffe000
Libc: 0x7ffff7a00000
With ASLR (on another run):
Stack: 0x7fffffc50000
Libc: 0x7ffff79c0000
→ The attacker’s exploit relying on a fixed address fails because locations change each execution.