September 27, 2026
Top 10 Security Vulnerabilities Every Developer Should Know in 2026
Security work is often invisible when it is done well. When everything is secure, users usually do not notice. But when a security issue isβ¦

By Code With Sunil | Code Smarter, not harder
7 min read
Security work is often invisible when it is done well. When everything is secure, users usually do not notice. But when a security issue is missed, the results can be serious β from data leaks and account takeovers to service downtime and financial loss.
With AI-generated code becoming more common, developers need a strong understanding of basic security principles. AI can help us write code faster, but developers are still responsible for making sure that code is safe and secure.
One of the best places to start learning application security is the OWASP Top 10. OWASP (Open Worldwide Application Security Project) is a nonprofit organization that provides security resources, guides, and best practices for developers.
In this article, we'll explore the top security vulnerabilities developers should know, understand why they happen, and learn practical ways to prevent them.
Let's get started.
1. Broken Access Control
Broken Access Control happens when an application allows users to access data or perform actions they are not authorized to use.
It remains one of the most important web application security risks. For example, User A should not be able to view User B's private invoice. Similarly, a normal user should not be able to access admin-only features.
Users should only be allowed to do what their permissions allow.
How to Prevent It
- Deny access by default: Allow access only when a user is explicitly authorized.
- Check permissions on the server: Never rely only on frontend checks.
- Reuse authorization logic: Keep access-control rules in a common place instead of duplicating them throughout the application.
- Check resource ownership: Make sure the requested record actually belongs to the current user.
- Invalidate sessions on logout: Do not allow old session identifiers to remain active.
- Use short-lived access tokens: This reduces the impact of stolen tokens.
- Configure CORS carefully: Allow only the origins that actually need access.
For example, suppose a user requests an invoice. Checking only whether the invoice exists is not enough. The application must also verify that the invoice belongs to the current user.
@Service
public class InvoiceService {
public Invoice getUserInvoice(Long invoiceId, Long userId) {
Invoice invoice = invoiceRepository.findById(invoiceId)
.orElseThrow(() -> new NotFoundException("Invoice not found"));
// Check that the invoice belongs to the current user
if (!invoice.getOwnerId().equals(userId)) {
throw new ForbiddenException("Access denied to this invoice");
}
return invoice;
}
}@Service
public class InvoiceService {
public Invoice getUserInvoice(Long invoiceId, Long userId) {
Invoice invoice = invoiceRepository.findById(invoiceId)
.orElseThrow(() -> new NotFoundException("Invoice not found"));
// Check that the invoice belongs to the current user
if (!invoice.getOwnerId().equals(userId)) {
throw new ForbiddenException("Access denied to this invoice");
}
return invoice;
}
}Here, getUserInvoice() first finds the invoice and then checks its ownership. If the invoice belongs to another user, the request is rejected.
2. Security Misconfiguration
Security Misconfiguration happens when an application or server is deployed with unsafe settings.
Common examples include exposed admin pages, detailed error messages, debug mode enabled in production, unnecessary services, and cloud resources with overly permissive access.
These problems are often caused by using default settings, forgetting to remove development features, or giving users more permissions than they need.
How to Prevent It
- Use secure defaults: Start with security enabled instead of relying on default configurations.
- Disable debug mode in production: Debug information can reveal sensitive details about your application.
- Hide detailed error messages: Show users simple error messages instead of stack traces or internal information.
- Remove unused features: Disable sample applications, test endpoints, and unnecessary services.
- Review cloud permissions: Make sure storage buckets, databases, and other resources are accessible only to the users or services that need them.
- Use the same secure baseline: Keep security settings consistent across development, testing, and production.
- Audit configurations regularly: Review application and cloud settings to find insecure configurations.
For example, in a Spring Boot application, you should avoid exposing detailed error information and unnecessary Actuator endpoints in production:
# application-prod.yml
server:
error:
include-message: never
include-stacktrace: never
include-binding-errors: never
management:
endpoints:
web:
exposure:
include: health,metrics
endpoint:
health:
show-details: never# application-prod.yml
server:
error:
include-message: never
include-stacktrace: never
include-binding-errors: never
management:
endpoints:
web:
exposure:
include: health,metrics
endpoint:
health:
show-details: neverHere, detailed error messages and stack traces are hidden from users. Only the required Actuator endpoints are exposed, and health details are not shown publicly.
3. Cryptographic Failures
Cryptographic Failures happen when sensitive data is not properly protected.
Common examples include using outdated algorithms such as MD5 or SHA-1, storing passwords in plain text, exposing secret keys, or committing private keys to source control.
How to Prevent It
- Use modern encryption and hashing algorithms.
- Never store passwords in plain text.
- Use Argon2 or bcrypt for password hashing.
- Keep API keys and secrets outside your source code.
- Use HTTPS to protect data in transit.
- Never commit private keys or passwords to Git.
For example, Spring Security provides BCryptPasswordEncoder for securely hashing passwords:
@Service
public class PasswordService {
private final PasswordEncoder passwordEncoder =
new BCryptPasswordEncoder(12);
public String hashPassword(String password) {
return passwordEncoder.encode(password);
}
public boolean verifyPassword(String password, String hash) {
return passwordEncoder.matches(password, hash);
}
}@Service
public class PasswordService {
private final PasswordEncoder passwordEncoder =
new BCryptPasswordEncoder(12);
public String hashPassword(String password) {
return passwordEncoder.encode(password);
}
public boolean verifyPassword(String password, String hash) {
return passwordEncoder.matches(password, hash);
}
}Here, the password is hashed before it is stored, and matches() safely checks the password during login.
Never store passwords or sensitive secrets in plain text.
4. Injection
Injection happens when untrusted user input is treated as part of a command or query instead of normal data.
A common example is SQL Injection, where an attacker sends malicious input that changes the SQL query.
Vulnerable Code
String query = "SELECT * FROM users WHERE email = '" + email + "'";String query = "SELECT * FROM users WHERE email = '" + email + "'";If user input is added directly to the query, an attacker may be able to change how the SQL statement works.
How to Prevent It
- Use parameterized queries or prepared statements.
- Validate user input.
- Use security features provided by your framework.
- Never build SQL queries by directly joining user input with strings.
Query("SELECT u FROM User u WHERE u.email = :email")
User findByEmail(@Param("email") String email);Query("SELECT u FROM User u WHERE u.email = :email")
User findByEmail(@Param("email") String email);Here, the user input is treated as data, not as part of the SQL command.
5. Software Supply Chain Failures
Software Supply Chain Failures happen when third-party libraries, packages, tools, or other components used by an application contain security risks.
For example, a developer may add a popular library to a project, but that library could later contain a vulnerability or be compromised by an attacker.
How to Prevent It
- Use packages from trusted sources.
- Keep dependencies updated.
- Pin dependencies to specific versions.
- Use lockfiles to keep versions consistent.
- Scan dependencies for known vulnerabilities.
- Remove libraries that are no longer needed.
For example, in a Maven project, specify a clear dependency version instead of relying on an unspecified or changing version:
<!-- Pin the dependency to a specific version -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.1</version>
</dependency><!-- Pin the dependency to a specific version -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.1</version>
</dependency>This makes the dependency version predictable and helps prevent unexpected changes.
6. Insecure Design
Insecure Design happens when security is not considered while designing a feature or system.
For example, imagine a document-sharing feature that allows users to open any document by changing its ID in the URL. Even if the code works correctly, the design is insecure because it does not properly consider ownership.
How to Prevent It
- Think about security before writing the code.
- Identify what users should and should not be allowed to do.
- Use threat modeling for important features.
- Ask, "What could an attacker do with this feature?"
- Test features for misuse, not just normal usage.
Security should be part of the design from the beginning, not added after the feature is built.
7. Authentication Failures
Authentication Failures happen when an application does not properly verify a user's identity.
Common examples include weak passwords, unlimited login attempts, insecure password-reset processes, and poorly managed sessions.
How to Prevent It
- Require strong passwords.
- Block commonly used passwords.
- Enable multi-factor authentication (MFA) when possible.
- Rate-limit login attempts.
- Use secure session management.
- Protect password-reset links and tokens.
- Return generic login error messages such as "Invalid credentials" to avoid revealing whether an account exists.
For example, you can rate-limit login attempts to slow down repeated password-guessing attempts:
@Service
public class AuthenticationService {
private final RateLimiter loginRateLimiter;
public AuthResult login(String email, String password) {
if (!loginRateLimiter.tryAcquire(email)) {
throw new TooManyRequestsException(
"Too many login attempts. Try again later."
);
}
// Verify the user's credentials here
return authenticateUser(email, password);
}
}@Service
public class AuthenticationService {
private final RateLimiter loginRateLimiter;
public AuthResult login(String email, String password) {
if (!loginRateLimiter.tryAcquire(email)) {
throw new TooManyRequestsException(
"Too many login attempts. Try again later."
);
}
// Verify the user's credentials here
return authenticateUser(email, password);
}
}Here, the application limits repeated login attempts for the same user. This makes automated password-guessing attacks much harder.
8. Software or Data Integrity Failures
Software or Data Integrity Failures happen when an application trusts code, packages, or data without verifying that they are safe and have not been modified.
For example, downloading software from an untrusted source or using a dependency without checking its integrity can introduce security risks.
How to Prevent It
- Use trusted package repositories.
- Verify digital signatures when available.
- Keep dependencies under control.
- Avoid downloading code from unknown sources.
- Use trusted and verified deployment artifacts.
9. Security Logging and Alerting Failures
Security Logging and Alerting Failures happen when an application does not properly record or monitor important security events.
For example, if an attacker repeatedly tries to log in or accesses sensitive data, your application should record the event and, when appropriate, alert the security team.
How to Prevent It
- Log important security events.
- Monitor failed login attempts and permission changes.
- Use centralized logging.
- Set alerts for suspicious activity.
- Protect logs from unauthorized changes.
- Never log passwords, tokens, or other sensitive information.
For example:
@Service
public class LoginService {
private static final Logger log =
LoggerFactory.getLogger(LoginService.class);
public void recordLogin(String email, boolean success) {
if (success) {
log.info("Successful login for user={}", email);
} else {
log.warn("Failed login attempt for user={}", email);
}
}
}@Service
public class LoginService {
private static final Logger log =
LoggerFactory.getLogger(LoginService.class);
public void recordLogin(String email, boolean success) {
if (success) {
log.info("Successful login for user={}", email);
} else {
log.warn("Failed login attempt for user={}", email);
}
}
}This simple logging helps developers identify unusual login activity.
10. Mishandling of Exceptional Conditions
Mishandling of Exceptional Conditions happens when an application does not handle unexpected errors or unusual situations safely.
Examples include exposing stack traces, failing to validate input, ignoring errors, or allowing an operation to continue when a security check fails.
How to Prevent It
- Handle errors safely and consistently.
- Validate input before processing it.
- Never expose stack traces to users.
- Log unexpected errors for debugging.
- Fail safely when something goes wrong.
- Test unusual and unexpected scenarios.
For example, Spring Boot can use a global exception handler to return a safe error message:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleError(Exception ex) {
// Log the real error internally
return ResponseEntity
.status(500)
.body("Something went wrong. Please try again.");
}
}@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ResponseEntity<String> handleError(Exception ex) {
// Log the real error internally
return ResponseEntity
.status(500)
.body("Something went wrong. Please try again.");
}
}The user receives a simple message, while the actual error can be logged internally for developers.
Security may not always be visible, but it is an important part of every application. Many common security problems can be prevented by following simple practices such as validating input, protecting sensitive data, managing access carefully, logging important events, and handling errors safely.
You don't need to become a security expert overnight. Start with the basics, review your applications, and fix the most important issues first.
Security is not a one-time task. It is an ongoing part of good software development.
Thanks for reading β see you in the next guide! π
As always, feel free to drop a comment if I've made any mistakes or if there's any way I can help!
Before you go: Be sure to clap π Share with other developers who might benefit from this solution! Follow the author to support: https://medium.com/@sunil17bbmp