OWASP Top 10 2025: How to Secure Your Code Part 3
In this series I’m going to go through the OWASP Top 10:2025 list with a snippet of vulnerable code relating to the vulnerability and show you how to secure it using Python.
Vulnerable Code
Below is a piece of vulnerable code which contains an Authentication Failure, Software or Data Integrity Failure, Security Logging and Alerting Failure, and Mishandling of Exceptional Condition vulnerability as listed on the OWASP Top 10:2025.
Note
The original vulnerable code was NOT generated by AI.
Authentication Failure
Authentication failure is when a bad actor is able to access or breach an application or system due to the application or system not properly verifying the legitimacy of the user. This can occur, but is not limited to:
- Credential Stuffing: This is when a bad actor is able to use automation to inject username and password pairs into an application or system. This is possible due to attackers having a list of breached username and password pairs. OWASP states this also includes hybrid password attacks credential stuffing which is also referred to as password spraying. This is where the attacker will use variations or incremented versions of the password, for example
Password1,Password2, and so on. - Brute Force Attacks: It allows bad actors to brute force or run other automated/scripted attacks on the system or application.
- Weak Passwords: Allows the user to create new accounts with well-known, weak, or previously breached passwords.
- Multi-factor Authentication: Has weak or missing multi-factor authentication.
- Session Identifier: Exposes a session identifier in a location accessible by the client or reuses the same session identifier after successful login.
- Single Sign-on Tokens (SSO): Does not correctly invalidate SSO tokens during logout or a timeout period.
Looking at the list above can you see where our application fails with authentication? There are two in our application. One is the fact that we’re not verifying the decoded JWT token, but this one also falls under Software or Data Integrity Failure so we’ll save it for that, the other is that we’re not checking newly created user passwords against weak passwords or previously breached passwords. There are different ways to do this, but the way I’m going to do it for the sake of this article is make sure the user’s password length is not shorter than 8 characters and make sure the password doesn’t equal some very common passwords.
Note
This is by no means the best way to do this. Depending on the application you’re building you might want to search through a local or hosted
rockyou.txtfile or use the HaveIBeenPwned API to query the first 5 characters of the hashed password against the API, HaveIBeenPwned’s api uses a technique calledK-Anonymity. You can read more about it here. The way I’ve decided to solve this vulnerability is for the sake of the article and to keep it simple and in-line with the simplicity of the vulnerable code.
Software or Data Integrity Failure
A software or data integrity failure is when code or infrastructure does not protect against untrusted code or data being used as trusted and valid. Some examples of this include having an insecure CI/CD pipeline which pulls code from untrusted sources or doesn’t verify code before using it. Having an application which auto-updates and doesn’t verify the integrity of the update before applying it to a previously trusted update. Lastly, having an application that relies on libraries, plugins, or modules from untrusted sources, or which uses deserialization functions which are vulnerable to attacks from bad actors such as pickle.loads which I talked about and secured here.
I stated the issue in the Authentication Failure section but the vulnerability here is that we’re not verifying the signature of the JWT token. This is a major security risk if used in this manner, but there are use cases where you don’t need to verify the JWT token two of which are:
- When there is a need to inspect metadata before verification just to make sure the payload has the expected data or to check if the signature is authentic.
- When there is a need to display the payload information on the client-side UI. For example if the payload contains a username and you want to use that username to welcome the user to the front-end application you can do so by decoding the payload.
JWT payloads are Base64URL-encoded strings so they can be forged but if your back-end is looking for a signature then even if a bad actor forges it on the client side they wouldn’t be able to do anything malicious on the back-end like deleting a database or accessing an endpoint they shouldn’t be able to access.
In our secure code we’ve added a signed encoded token to our login endpoint and then properly decode it with our signature to make sure it is verified.
Security Logging and Alerting Failure
A security logging and alerting failure occurs when an application or system fails to log, monitor, detect, or alert on any breaches. Logging and monitoring should occur when a user logs in to an application or system successfully or unsuccessfully, unauthorized or suspicious access to a page or part of the application or system successfully or unsuccessfully, important system events, unhandled exceptions, or application errors, and many more examples which can be found on the OWASP security logging and alerting failure page.
As you can see our application lacks any sort of logging so we’re going to add some logging. What we’re going to do is create a log file using Python’s logging module and then log any relevant successes and failures to our log file. Here is how we initialize our log file and log any necessary information. To see everything we’ve logged you can view the python file in full in the link at the end of this article.
Mishandling of Exceptional Condition
Mishandling of exceptional conditions happens when programs fail to prevent, detect, and respond to unpredictable inputs, unusual states, runtime errors, or system failures. This can lead to crashes, unexpected behaviour, sensitive data leaks, resource exhaustion, and in some cases vulnerabilities. Some vulnerabilities that can occur are logic bugs, race conditions, overflows, issues with memory states, timing, authentication and authorization. Can you see where our error is?
Inside the username_query function we’re catching an exception and printing it to our user without filtering the error from the database. This is a dangerous practice because we could be leaking information from our database or information pertaining to our tech stack which could allow bad actors to find vulnerabilities based on the tech we’re using or the versions of the tech we’re using.
The way to fix this is to let the user know there has been an internal server error and then we can log the error from the database into our internal logging to allow admins and security professionals to see what the issue is without leaking anything to potential bad actors.
This concludes the second part of the article on securing OWASP Top10:2025 security vulnerabilities. You can find the vulnerable and secure code versions on my github here.








