The Open Web Application Security Project (OWASP), now commonly referred to as Open Worldwide Application Security Project, is an online non-profit community saddled with the responsibilities of improving the security of software applications. They achieve this by setting standards, including resources, documentation, tools, and methodologies, and making them freely available for the Tech communities and the public at large.
In 2003, the OWASP community first released what is now known as the OWASP Top 10 security awareness standard. A document aimed at helping developers and security professionals understand, prevent, and mitigate vulnerabilities. The report has continued to be updated to date at intervals of two to three years, in line with advancements in technology and security exposures.
The OWASP Top 10 has, over the years, undergone different updates and reviews, and the top 10 list currently stands at the below, which is based on OWASP 10:2021 edition:
- Broken Access Control
- Cryptographic Failures
- Injection
- Insecure Design
- Security Misconfiguration
- Vulnerable and Outdated Components
- Identity and Authentication Failures
- Software and Data Integrity Failures
- Security Logging and Monitoring Failures
- Server-Side Request Forgery (SSRF)
We will be taking a look at them, one after another, and how they apply to application development, with some code snippet examples where applicable.
1. Broken Access Control
Access Control is a situation where there is a proper check to determine who gets access to a system or an application and what actions/functionalities they can perform. Broken Access Control refers to a condition/vulnerability where an unauthorized person or an attacker can have access to a system or perform certain actions beyond their privilege. This is due to no validation of logged-in users or improper enforcement of access restrictions.
This sounds harmless or trivial but imagine an e-commerce web application or banking app with a broken access control. It exposes the business to a great danger and loss. Examine these Java code snippets and how few additional lines of code were able to save the day.
Code Snippet with a Broken Access Control
@WebServlet(“/profile”)
public class UserProfileServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// User ID taken directly from the request (insecure!)
String userId = request.getParameter(“id”);
// Simulate fetching user data
String profileData = UserService.getProfile(userId);
response.getWriter().write(“Profile: ” + profileData);
} }
Code Snippet with Validation and Authorization checks that fixed the Broken Access Control
@WebServlet(“/profile”)
public class UserProfileServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
// Don’t create a session if it doesn’t exist
HttpSession session = request.getSession(false);
// Step 1: Check if user is logged in
if (session == null || session.getAttribute(“userId”) == null) {
// 401
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.getWriter().write(“Unauthorized: Please log in.”);
return;
}
// Step 2: Fetch logged-in user ID from session
String loggedInUserId = (String) session.getAttribute(“userId”);
// Step 3: Fetch and return user’s own profile
String profileData = UserService.getProfile(loggedInUserId);
response.getWriter().write(“Profile: ” + profileData);
} }
2. Cryptographic Failures
Sitting in the second position is Cryptographic Failures. It refers to failures in cryptography or its absence, which often result in the exposure of sensitive data like passwords, credit card numbers, healthcare records, or personal information. It encompasses issues rooted in poor implementation, weak algorithms, or improper key management rather than just data exposure as a symptom.
As concerns the Applications and saving data to the Database, Cryptographic Failures occur when Applications fail to properly protect sensitive data in transit or at rest. This includes transmitting data in clear text (e.g., over HTTP, SMTP, or FTP instead of secure protocols), using outdated or weak cryptographic algorithms/protocols (like MD5 or SHA-1 for hashing), employing default or weak crypto keys that might be hard-coded or poorly managed, and not enforcing encryption where needed (e.g., missing HTTP security headers like Strict-Transport-Security). Other common issues involve improper server certificate validation, reusing initialization vectors insecurely, using non-cryptographic random number generators, or generating exploitable error messages from cryptographic operations. These failures can violate privacy laws like the EU’s GDPR or financial regulations like PCI DSS, leading to data breaches.
To guard against this menace, several ways have been highlighted, which include, but are not limited to:
- Store passwords using strong, adaptive, salted hashing functions like Argon2, scrypt, bcrypt, or PBKDF2 with a high work factor (e.g., 310,000 iterations for PBKDF2).
- Encrypt all sensitive data at rest using strong, up-to-date algorithms.
- Encrypt data in transit with secure protocols like TLS (prefer forward secrecy ciphers and enforce via HSTS); avoid legacy protocols for sensitive transmissions.
- Application developers should take care to ensure that they are not logging out sensitive data or information. This should be checked by the AppSec Engineer during code review, and while in production, before allowing for public use.
Code Snippet with Cryptographic Failure
public class VulnerableHash {
public static String hashPassword(String password) throws NoSuchAlgorithmException{
// BAD: MD5 is cryptographically broken and unsuited for password hashing — use Argon2/PBKDF2/scrypt with a cost factor
MessageDigest md = MessageDigest.getInstance(“MD5”);
// BAD: no salt and only a single fast iteration (and uses default charset) — enables rainbow/brute‑force attacks
byte[] messageDigest = md.digest(password.getBytes());
// questionable: unnecessary conversion to BigInteger (can mask byte semantics); not a security feature
BigInteger no = new BigInteger(1, messageDigest);
String hashtext = no.toString(16);
while (hashtext.length() < 32) {
// style/brittle: manual padding instead of using a proper hex encoder
hashtext = “0” + hashtext;
}
//BAD: returned value contains no algorithm/salt/iterations metadata — makes upgrades and secure verification harder
return hashtext;
} }
Code Snippet without Cryptographic Failure
public class SecureHash {
public static String hashPassword(String password) throws NoSuchAlgorithmException, InvalidKeySpecException {
int iterations = 100000; // High work factor
char[] chars = password.toCharArray();
byte[] salt = new byte[16];
SecureRandom random = new SecureRandom();
random.nextBytes(salt);
PBEKeySpec spec = new PBEKeySpec(chars, salt, iterations, 64 * 8);
SecretKeyFactory skf = SecretKeyFactory.getInstance(“PBKDF2WithHmacSHA256”);
byte[] hash = skf.generateSecret(spec).getEncoded();
// Convert to hex for storage (in practice, store salt:hash)
BigInteger saltBigInt = new BigInteger(1, salt);
BigInteger hashBigInt = new BigInteger(1, hash);
return saltBigInt.toString(16) + “:” + hashBigInt.toString(16);
} }
3. Injection
Injection attacks occur when untrusted user input is interpreted as part of a command or query, allowing attackers to manipulate the application’s behavior. Common types include SQL Injection, Command Injection, and Code Injection, where malicious input can lead to unauthorized data access, data manipulation, or code execution.
Key points:
- Cause: Insufficient input validation or improper handling of user input in queries or commands.
- Impact: Can lead to data breaches, unauthorized access, or full system compromise.
- Prevention: Use parameterized queries, input sanitization, and secure APIs to mitigate risks.
Code Snippet with Injection Vulnerability
public class VulnerableExample {
public void getUser(Connection conn, String userInput) throws SQLException {
// User input is directly concatenated into the query
String query = “SELECT * FROM users WHERE username = ‘” + userInput + “‘”; Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(query);
while (rs.next()) { System.out.println(“User: ” + rs.getString(“username”)); }
} }
Code Snippet without Injection Vulnerability
public class SecureExample { public void getUser(Connection conn, String userInput) throws SQLException {
// Use PreparedStatement to parameterize the query
String query = “SELECT * FROM users WHERE username = ?”;
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, userInput); // Safely bind user input
ResultSet rs = pstmt.executeQuery();
while (rs.next()) { System.out.println(“User: ” + rs.getString(“username”)); } } }
4. Insecure Design
This principle is more concerned with the flaws in the application’s design that create security vulnerabilities. Unlike implementation bugs, insecure design stems from missing or ineffective security controls in the system’s architecture, making it prone to exploitation even if coded correctly. It highlights the need for secure design principles, such as threat modeling and secure-by-design practices, during the development lifecycle.
Key points:
- Cause: Lack of security considerations in the design phase, such as missing access controls or improper data validation assumptions.
- Impact: Can lead to systemic vulnerabilities, like unauthorized access or data exposure, that are hard to fix post-development.
- Prevention: Incorporate threat modeling, secure design patterns, and regular security reviews early in the development process.
Code Snippet with Insecure Design Code (Missing Access Control)
public class InsecureDesignExample {
public String getSensitiveData(int userId) {
// No access control check; any user can access any data
Database db = new Database();
return db.query(“SELECT sensitive_data FROM records WHERE user_id = ” + userId);
} }
Code Snippet with Secure Design Code (Proper Access Control)
public class SecureDesignExample {
public String getSensitiveData(int userId, int requestingUserId, String userRole) {
// Check if the requesting user is authorized
if (!isAuthorized(requestingUserId, userId, userRole)) {
throw new SecurityException(“Unauthorized access”);
}
Database db = new Database();
return db.query(“SELECT sensitive_data FROM records WHERE user_id = ” + userId);
}
private boolean isAuthorized(int requestingUserId, int targetUserId, String userRole) {
// Example: Only allow users to access their own data unless they are admins
return requestingUserId == targetUserId || “ADMIN”.equals(userRole);
}}
5. Security Misconfiguration
This focuses more on settings that can reveal sensitive information. It occurs when an application, server, or system is not securely configured, leaving it exposed to attacks. This can include default settings, unnecessary features enabled, exposed error messages, or misconfigured permissions that attackers can exploit to gain unauthorized access or manipulate the system.
Common Issues:
- Default credentials unchanged (e.g., admin/admin).
- Exposed stack traces or verbose error messages revealing sensitive information.
- Unnecessary services or ports left open.
- Misconfigured HTTP headers or CORS policies.
- Missing security patches or outdated software.
Impact: Attackers can exploit these misconfigurations to gain unauthorized access, steal data, or compromise the system entirely.
Insecure Code (Security Misconfiguration exposing full stack trace)
public class InsecureServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(“text/html”);
PrintWriter out = response.getWriter();
try {
// Simulate an error
String input = request.getParameter(“data”);
int number = Integer.parseInt(input); // May throw NumberFormatException
out.println(“Number: ” + number);
} catch (Exception e) {
// Exposing stack trace in response (Security Misconfiguration)
out.println(“Error: ” + e.toString());
// Exposes detailed stack trace
e.printStackTrace(out);
} } }
The servlet catches an exception but outputs the full stack trace (e.printStackTrace(out)), which may include sensitive details like file paths, library versions, or database information. Attackers can use this information to identify vulnerabilities or plan targeted attacks.
Secure Code with Proper Configuration
public class SecureServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(“text/html”);
PrintWriter out = response.getWriter();
try {
// Simulate an error
String input = request.getParameter(“data”);
// May throw NumberFormatException
int number = Integer.parseInt(input);
out.println(“Number: ” + number);
} catch (Exception e) {
// Log the error internally (e.g., to a secure log file)
log(“Error processing request: ” + e.getMessage());
// Display generic error message to user
out.println(“An error occurred. Please try again or contact support.”);
}
} }
The servlet avoids exposing the stack trace or detailed error information to the user. Errors are logged internally (log() method) for developers to review securely, while users see only a generic message. This reduces the risk of leaking sensitive information that could aid attackers.
To mitigate against Security Misconfiguration, it is advisable to:
- Disable Debugging: Turn off detailed error messages in production (e.g., set debug=false in web frameworks).
- Use Secure Defaults: Change default credentials, disable unnecessary features, and apply least privilege principles.
- Harden Configurations: Configure HTTP headers (e.g., Content-Security-Policy, X-Frame-Options) and restrict CORS policies.
- Regular Audits: Continuously monitor and audit configurations for servers, frameworks, and dependencies.
6. Vulnerable and Outdated Components
This is a critical risk. It happens when a developer uses software components (libraries, frameworks, dependencies, or modules) that are outdated, unmaintained, or have known vulnerabilities. Attackers can exploit these weaknesses to compromise systems, leading to data breaches, unauthorized access, or other attacks. Risks arise when developers fail to:
- Monitor and update components regularly.
- Use components with known vulnerabilities (e.g., listed in CVE databases).
- Track dependencies in the software supply chain.
Key Points:
- Why it’s a risk: Outdated components may contain unpatched vulnerabilities exploitable by attackers.
- Examples: Using an old version of Apache Struts or Log4j with known CVEs.
- Mitigation: Regularly update components, use software composition analysis (SCA) tools, monitor vulnerability databases (e.g., NVD), and remove unused dependencies.
7. Identification and Authentication Failures
Identification and Authentication Failures refer to vulnerabilities where an application fails to properly verify a user’s identity or maintain secure authentication processes. This can lead to attackers gaining unauthorized access by exploiting weak passwords, a lack of multi-factor authentication (MFA), session mismanagement, or credential stuffing. Common issues include:
- Weak password policies
- Insecure session handling (e.g., predictable session IDs)
- Lack of MFA
- Exposure of credentials in error messages or logs
- Failure to invalidate sessions after logout
To mitigate Identification and Authentication Failures, implement strong password policies, use secure hashing (e.g., bcrypt), enforce MFA, manage sessions securely, and limit login attempts.
8. Software and Data Integrity Failures
This refers to vulnerabilities where software or data integrity is compromised due to insufficient validation or protection mechanisms. This can include issues like:
- Insecure Deserialization: Accepting untrusted data without proper validation, leading to malicious code execution or data tampering.
- Lack of Integrity Checks: Failing to verify the integrity of software updates, dependencies, or data (e.g., no checksums or digital signatures).
- Supply Chain Attacks: Using compromised third-party libraries or components that introduce malicious code.
- Unverified CI/CD Pipelines: Deploying software without ensuring the integrity of the build process or artifacts.
These failures can allow attackers to manipulate data, execute unauthorized code, or compromise systems by exploiting unverified or tampered inputs, updates, or dependencies.
To guard against this, software engineers and application developers should use digital signatures to verify updates, check their software supply chain, and ensure that continuous integration/continuous deployment (CI/CD) pipelines have strong access control and are configured correctly.
9. Security Logging and Monitoring Failures
This situation refers to insufficient logging and monitoring practices that hinder detection, escalation, and response to security incidents. This vulnerability occurs when applications fail to log critical events (e.g., login attempts, errors, or suspicious activities) or when logs are not monitored effectively, allowing attackers to operate undetected. It can lead to delayed incident response, making it easier for attackers to compromise systems, escalate privileges, or extract data.
Key Issues:
- Inadequate Logging: Failing to log important events like failed login attempts, access control violations, or input validation errors.
- Lack of Monitoring: Not actively reviewing logs or setting up alerts for suspicious activities.
- Improper Log Storage: Storing logs insecurely, allowing attackers to tamper with or delete them.
- Missing Context: Logs lacking sufficient detail (e.g., user ID, timestamp, or IP address) to trace incidents.
Impact: Attackers can persist in the system longer, perform brute-force attacks, or exploit vulnerabilities without being noticed.
Prevention:
- Log all security-relevant events (e.g., authentication attempts, errors, access control failures).
- Ensure logs are detailed, tamper-proof, and stored securely.
- Implement real-time monitoring and alerting for suspicious activities.
- Use centralized logging systems for easier analysis.
10. Server-Side Request Forgery
Server-Side Request Forgery (SSRF) is a security vulnerability where an attacker tricks a server into making unintended HTTP requests to arbitrary destinations. This can lead to accessing internal systems, sensitive data, or external services not meant to be exposed. SSRF exploits occur when a server accepts user-supplied URLs or inputs without proper validation, allowing attackers to manipulate the server into sending requests to malicious or unauthorized endpoints.
Key Risks of SSRF:
- Accessing internal services (e.g., metadata endpoints in cloud environments).
- Bypassing firewalls or network restrictions.
- Scanning internal networks or external systems.
- Data leakage or service disruption.
Example Scenarios:
- An attacker provides a URL like http://169.254.169.254/latest/meta-data/ to access cloud instance metadata.
- Forcing the server to connect to internal services like http://localhost/admin.
SSRF Mitigation Strategies as recommended by OWASP:
- Use an Allowlist: Restrict requests to a predefined list of trusted domains or IPs.
- Block Internal IPs: Prevent requests to localhost, private IP ranges (e.g., 10.0.0.0/8, 192.168.0.0/16), or cloud metadata endpoints.
- Input Validation: Sanitize and validate user input before making requests.
- Disable Unused Protocols: Restrict protocols to HTTP/HTTPS and block file, ftp, or other schemes.
- Network Segmentation: Limit the server’s access to internal systems.
By implementing these practices, as shown in the safe code example, SSRF vulnerabilities can be effectively mitigated.
In conclusion, it is strongly recommended that organizations prioritize these practices by placing security at the forefront of their operations and embedding a culture of security by design. Doing so will not only foster a secure environment in which businesses can operate and grow with confidence but also enhance resilience against potential threats, build stakeholder trust, and support long-term success.

Leave a Reply