Application & Cloud Security

Application Security

A practical guide to building software that handles input safely, protects users correctly, and reduces risk before code reaches production.

Application security focuses on identifying and fixing vulnerabilities in software before and after it reaches production. In a DevSecOps culture, security is woven into every phase of the development lifecycle rather than bolted on as a final gate.

Learning objectives

What you should be able to do after reading.
  • Explain the core application-layer controls that shape secure software design.
  • Recognize how input handling, identity checks, and session management affect risk.
  • Describe the most common web risks and the development habits that reduce them.

At a glance

Fast mental model before you dive in.
Core controls
  • Input validation
  • Authentication
  • Authorization
Operational risk
  • Session handling
  • Secrets management
  • Dependency awareness
Secure habits
  • Safe defaults
  • Least privilege
  • Reviewable changes

Threat Modeling

Threat modeling is the practice of systematically identifying what could go wrong with an application before writing a single line of code. Teams diagram data flows, trust boundaries, and entry points, then enumerate threats using frameworks such as STRIDE (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege). The output is a prioritized list of mitigations that drives design decisions and test cases.

The STRIDE framework gives engineers a structured lens for every component and data flow. For each element, the team asks. Can this be spoofed, can data be tampered with in transit, can an actor deny an action, could information be disclosed, could service be denied, could a user gain unintended privileges? Every affirmative answer becomes a candidate mitigation tracked in the backlog.

Threat modeling is most valuable when conducted early in the design phase, before significant implementation begins. A threat discovered at the design stage requires a change to a diagram, the same threat discovered after deployment requires a patch, a migration, and potentially an incident response. Teams that embed threat modeling into their standard design review catch the most expensive problems at the cheapest moment.

Static Application Security Testing (SAST)

SAST tools analyse source code without executing it, flagging patterns associated with common vulnerabilities such as SQL injection, cross-site scripting, insecure deserialization, and hardcoded credentials. Popular tools include Semgrep, Checkmarx, SonarQube, and Bandit for Python. Integrating SAST into a CI pipeline means every pull request is automatically scanned before it is merged.

SAST works by parsing source code into an Abstract Syntax Tree or control-flow graph, then applying detection rules that match known vulnerable patterns. Custom rules can target organisation-specific frameworks and deprecated internal APIs that generic rulesets miss. Running SAST on every commit gives developers immediate feedback while the context of their change is still fresh.

Managing false positives is the ongoing challenge of any SAST programme. Tools that surface hundreds of warnings per scan quickly produce alert fatigue, where developers dismiss all findings including genuine issues. Effective programmes invest in rule tuning, suppressions for confirmed false positives, and a triage process that routes real findings to the developer who owns the code.

Dynamic Application Security Testing (DAST)

DAST tools interact with a running application the same way an attacker would, sending crafted inputs and inspecting responses for signs of vulnerabilities. OWASP ZAP and Burp Suite are widely used DAST tools. Because DAST requires a deployed target, it typically runs against a staging or integration environment rather than directly against production.

DAST finds runtime vulnerabilities that static analysis cannot reach. Authentication configuration flaws, session management issues, insecure HTTP headers, server errors that leak internal paths, and injection vulnerabilities that only manifest when the full stack runs together. It validates the application as it actually behaves, not just as it was written.

A typical DAST pipeline phase uses a spider to discover application paths, then an active scan that sends attack payloads to each endpoint. Paths that require authentication or specific input sequences may be missed unless the scanner is configured with credentials and user flows. Combining automated DAST with periodic manual penetration testing closes this coverage gap.

Software Composition Analysis (SCA)

Modern applications depend heavily on open-source libraries. SCA tools scan dependency manifests and lock files, matching components against vulnerability databases such as the National Vulnerability Database and OSV. Tools like Snyk, Dependabot, and OWASP Dependency-Check alert teams when a direct or transitive dependency carries a known vulnerability.

The key risk is transitive dependencies, a project with ten direct packages may pull in hundreds of transitive ones, each a potential exposure. The Log4Shell vulnerability (CVE-2021-44228) made this concrete. Millions of applications were affected not because they directly depended on Log4j, but because it was a transitive dependency of a framework they used. Visibility into the full dependency tree is not optional.

SCA tools also generate Software Bills of Materials (SBOMs), machine-readable inventories of every component in an application. SBOMs enable rapid triage when new vulnerabilities are disclosed. Rather than auditing each application by hand, a team queries the SBOM inventory and immediately identifies every affected system. Generating SBOMs is increasingly required by government procurement policy and supply chain security standards.

Secure Coding Practices

Beyond tooling, developers need a baseline of secure coding knowledge. Key principles include input validation, output encoding, parameterised queries, the principle of least privilege, and error handling that avoids leaking stack traces or internal paths to users. These principles address the root causes of the most common vulnerability classes.

The OWASP Top 10 is a widely-used taxonomy of the most impactful web vulnerability classes. Understanding injection, broken access control, cryptographic failures, and security misconfiguration gives developers a mental model for recognising vulnerable patterns before they write them. OWASP cheat sheets provide concise, language-specific guidance for implementing each mitigation correctly.

Security champions programmes embed security-minded engineers within each product team, creating peer-level resources for security questions and normalising security as a shared engineering responsibility. Champions are not gatekeepers who review code after the fact, they are advisors who help teams make better decisions during design and implementation, before vulnerabilities are created.

Signals to watch for

Patterns worth investigating further.
  • User input reaches business logic without clear validation or normalization.
  • Access checks are inconsistent across routes, handlers, or services.
  • Secrets, tokens, or credentials appear in code, logs, or configuration files.

DEEP DIVE

Secure Coding Basics

Secure coding is the practice of writing software with security treated as a first-class requirement alongside functionality. Rather than hardening code after the fact, secure coding integrates defensive thinking into every function, every input handler, and every data flow. The goal is code that is correct and secure simultaneously, not fast code that is patched later.

The foundation is understanding the trust model of your application. Every piece of data that crosses a trust boundary, arriving from a user, a partner API, a message queue, or a database, must be treated as potentially hostile until validated. The fact that data came from your own database does not make it safe if that database could contain values injected by a previous attacker.

Core principles are not complicated. Validate all input, encode all output for its destination context, use parameterized queries for database access, apply least privilege in all resource requests, fail safely on errors, log security events without logging sensitive values, and never implement cryptographic primitives yourself. These principles address the root causes of the most common vulnerability classes.

The most persistent mistake is assuming a framework handles security automatically. Frameworks prevent many mistakes but cannot protect against every misuse. A developer who uses an ORM incorrectly can still introduce SQL injection. A developer who disables auto-escaping in a templating engine can still introduce XSS. Security requires understanding, not just selecting the right tool.

Input Validation

Input validation is the process of verifying that data supplied from external sources conforms to the expected format, type, length, and range before the application processes or stores it. Allowlist validation (sometimes called whitelist validation) defines exactly what is acceptable and rejects everything else. Denylist validation attempts to reject known-bad patterns. Allowlist validation is almost always stronger, because it is impossible to enumerate every possible attack pattern.

Virtually every injection attack exploits code that processes untrusted input without validation. SQL injection sends database syntax in form fields. Command injection sends shell metacharacters. Path traversal uses sequences like '../' to escape intended directories. Cross-site scripting sends HTML and JavaScript that is later reflected in a page. In every case the root cause is the same. External data was treated as trusted without being checked first.

Server-side validation is mandatory regardless of what client-side validation exists. Client-side validation (JavaScript or HTML input constraints) improves user experience but provides no security guarantee. Any attacker can bypass client-side validation by sending HTTP requests directly to the server using tools like curl or Burp Suite. If validation only happens in the browser, the server is completely unprotected.

Validation must happen at the point of receipt, before data is used in any security-sensitive operation. A common mistake is validating at the UI layer but then passing data through several service layers before it reaches the database, with each layer assuming the previous one validated. If any layer forwards unvalidated data, the protection breaks. Validate as close to the source as possible, and enforce again at the consumption point for high-value operations.

Authentication

Authentication is the process of verifying that a user, service, or device is who it claims to be. In web applications this typically means verifying a credential (password, token, certificate) against a stored record. Broken authentication is consistently among the top vulnerability classes in web applications because the consequences of failure are severe, an attacker who bypasses authentication can impersonate any user, including administrators.

Strong password-based authentication requires several properties working together. Passwords must be stored using a hashing algorithm designed for password storage (bcrypt, scrypt, or Argon2), not MD5 or unsalted SHA-1. Password reset flows must use time-limited tokens sent to a verified channel, not security questions. Account lockout or rate limiting must prevent brute-force attacks. Plaintext storage, even in internal systems, is never acceptable.

Multi-factor authentication (MFA) dramatically reduces account compromise risk. Even if a password is stolen through phishing, a credential database breach, or password reuse, MFA requires the attacker to also possess a second factor, a TOTP code from an authenticator app, a hardware security key (FIDO2/WebAuthn), or a one-time code sent to a verified phone. Hardware security keys are the strongest option because they are phishing-resistant by design.

A common authentication mistake is implementing a password reset flow weaker than the login flow itself. If an attacker can reset any account's password knowing only the email address, authentication strength is defined by the weakest link. Another common mistake is not invalidating existing sessions after a password change, leaving old tokens active even after the account credential changes. True password reset must invalidate all previously issued sessions.

Authorization

Authorization is the process of determining whether an authenticated identity is permitted to perform a specific action or access a specific resource. Authentication answers who are you, authorization answers what are you allowed to do. Broken access control is the number one vulnerability class in the OWASP Top 10, meaning authorization failures are more common and more impactful than any other vulnerability class.

Role-Based Access Control (RBAC) assigns permissions to roles and roles to users. Attribute-Based Access Control (ABAC) makes decisions based on attributes of the user, the resource, and the environment (time of day, IP address, data classification level). In either model the critical requirement is that checks happen server-side for every request, not just once at login.

Insecure Direct Object Reference (IDOR) is the most common access control vulnerability. It occurs when an application uses a user-supplied value (a numeric ID, a UUID, a filename) to look up a resource without verifying that the requesting user is authorized to access that specific record. An attacker who notices their account ID appears in a URL can try substituting other IDs and may find the server returns other users' data without any ownership check.

A fundamental mistake in authorization design is confusing obscurity with access control. Hiding an administrative endpoint behind a long, random path is not authorization, if the URL is ever discovered through browser history, error messages, or referrer headers, it provides unrestricted access to anyone who finds it. Every sensitive endpoint must have an explicit authorization check regardless of how difficult it is to discover. Security through obscurity is never a substitute for proper access control.

Session Issues

Session management covers how an application maintains continuity for an authenticated user across multiple HTTP requests. Because HTTP is stateless, applications issue a session token after successful login and the client presents this token with every subsequent request. The security of the entire authenticated session depends on this token, an attacker who obtains a valid token has the same access as the legitimate user without ever knowing the password.

Secure session tokens must be long (at least 128 bits of cryptographic randomness), generated by a cryptographically secure random number generator, and transmitted only over HTTPS. Cookies carrying session tokens should have the HttpOnly flag (preventing JavaScript from reading the cookie value) and the Secure flag (preventing transmission over plain HTTP). The SameSite attribute should be set to Strict or Lax to mitigate cross-site request forgery.

Session fixation is an attack where the attacker tricks the victim into authenticating with a token the attacker already knows. The defense is to always generate a fresh session token immediately after successful authentication, replacing any token that existed before login. Privilege elevation within a session, such as re-authenticating before a sensitive operation, should also trigger a new token.

Not invalidating sessions server-side at logout is a critical and common mistake. If the server simply instructs the client to delete its cookie without invalidating the server-side session record, the token remains valid indefinitely. An attacker who captured the token before logout can continue using it. True logout must invalidate the session on the server so that the token is useless even if an attacker possesses it.

Common Web Risks

The OWASP Top 10 lists the most critical web application security risks, updated periodically to reflect the current threat landscape. The 2021 edition covers broken access control, cryptographic failures, injection, insecure design, security misconfiguration, vulnerable and outdated components, identification and authentication failures, software and data integrity failures, security logging and monitoring failures, and server-side request forgery (SSRF). Each category shares root causes and similar mitigations.

Cross-Site Scripting (XSS) occurs when an application includes untrusted data in a web page without proper encoding, allowing attackers to inject JavaScript that executes in the victim's browser. Stored XSS (where the malicious script is saved to the database) is especially dangerous because every user who views the affected content is attacked. The defense is context-sensitive output encoding. HTML encoding for HTML content, JavaScript encoding for script contexts, URL encoding for URL parameters.

Server-Side Request Forgery (SSRF) is a vulnerability where an attacker causes the server to make HTTP requests to attacker-controlled destinations. This is dangerous in cloud environments because the instance metadata service (which provides temporary cloud credentials) is accessible only from the instance itself. An attacker who reaches the metadata service via SSRF can steal the instance's cloud credentials and escalate to full cloud account access.

A common conceptual confusion is treating XSS and CSRF as the same attack. XSS attacks the user by injecting malicious content into pages they view. CSRF attacks the server by tricking the user's browser into making unintended requests. XSS defenses focus on output encoding. CSRF defenses focus on verifying request origin using tokens or the SameSite cookie attribute. Understanding the distinction is necessary to apply the correct mitigation for each.

Secrets in App Code

Secrets in application code refers to the antipattern of embedding credentials, API keys, passwords, or cryptographic keys directly in source code, configuration files, or build artifacts that are committed to version control. This is one of the most common and most exploited security mistakes in software development. Credentials embedded in code are discovered through repository scanning, breaches, or accidental publication of repositories.

The git history problem makes this mistake particularly hard to recover from. Even if a developer removes a secret from the latest commit, it remains accessible in git history to anyone with repository access. Tools like 'git log -S' and 'git diff' can recover any string ever committed. The only safe remediation after a secret has been committed is to rotate the secret immediately, because the old value must be assumed compromised.

The correct pattern is to load secrets from the environment at runtime, not from source code. Use environment variables injected by the deployment system, or retrieve secrets from a dedicated vault such as HashiCorp Vault or the cloud provider's secret manager. The application code references a configuration key, the deployment system maps that key to the actual secret value at runtime. No secret value ever appears in any committed file.

Secret scanning tools detect credentials in version control. Tools like Gitleaks, TruffleHog, and GitHub's built-in secret scanning run automatically on commits and alert when patterns matching API keys, connection strings, private keys, or other sensitive values are detected. Running these checks as a pre-commit hook catches secrets before they are committed, running them in CI catches any that slip through. Retroactively scanning the full repository history is also important, not just new commits.

Dependency Risk at App Level

Dependency risk is the security exposure introduced by the third-party libraries, frameworks, and packages an application depends on. Modern applications typically rely on dozens to hundreds of open-source packages, each of which may contain known or unknown vulnerabilities. The application inherits the security posture of every dependency it uses, whether directly declared or pulled in transitively by another package.

The scale of this risk became dramatically visible with the Log4Shell vulnerability (CVE-2021-44228) in December 2021. A single critical vulnerability in the Log4j logging library affected millions of applications worldwide. Attackers were actively exploiting it within hours of disclosure. Teams that maintained accurate dependency inventories identified and patched affected systems in hours, teams without this visibility spent days determining whether they were affected.

Software Composition Analysis (SCA) tools address this risk by continuously scanning the full dependency tree (including transitive dependencies) against vulnerability databases. When a new vulnerability is disclosed, SCA tools can immediately report which applications are affected and at what severity. Integrating SCA into CI pipelines means pull requests that introduce vulnerable dependencies are blocked before they merge.

Keeping dependencies up to date requires a strategy, not ad-hoc manual effort. Automated update tools (Dependabot, Renovate) open pull requests when newer versions are available, keeping the review process manageable. Pinning exact dependency versions using lock files ensures reproducible builds but requires active update management. The most dangerous position is unpinned, unmonitored dependencies that silently drift behind on security patches.