OWASP Top 10 explained for freshers: why each bug happens and how to fix it
By VA2PT Team, . 9 min read

The OWASP Top 10 is the single best starting point for anyone learning application security. It is a ranked list of the most common and most serious web application risks, maintained by a global community and updated every few years. If you understand these ten categories, you understand the majority of what a penetration test looks for and the majority of what goes wrong in real breaches.
This article explains each category in plain terms: what it is, why it happens, and how a developer fixes it. We go deepest on the two that freshers meet first, injection and cross-site scripting, because understanding those two teaches you the pattern behind almost all of the others.
Practise legally. Every bug below can be studied safely on deliberately vulnerable training apps such as OWASP Juice Shop, DVWA, and the PortSwigger Web Security Academy, which exist precisely so you can learn without touching systems you do not own.
The one idea behind most web bugs
Before the list, learn the pattern that connects most of it: trust boundaries. A trust boundary is any point where data crosses from a place you do not control (a user's browser, another service, a file upload) into a place you do (your database, your page, your server's shell). Almost every serious web bug is data crossing a trust boundary without being checked or encoded for where it is going. Once you see that, the fixes stop feeling like a list of tricks and start feeling like one habit.
The categories
A01: Broken access control
What it is: users can do or see things they should not, such as opening another customer's invoice by changing an ID in the URL.
Why it happens: the application checks who you are (authentication) but forgets to check what you are allowed to do (authorisation) on every request. It is easy to protect the page that lists your own orders and forget that /orders/1043 can be typed directly.
How to fix it: enforce authorisation on the server for every object, on every request. Never rely on the UI hiding a button. Check ownership: "does this order belong to the logged-in user?" This class of bug, often called IDOR (insecure direct object reference), is one of the most common findings in any test.
A02: Cryptographic failures
What it is: sensitive data exposed because it was not encrypted, or was encrypted badly.
Why it happens: passwords stored with fast or reversible hashing, traffic served over plain HTTP, secrets in plain text, or home-grown "encryption".
How to fix it: use TLS everywhere, store passwords with a slow, salted hash such as bcrypt or Argon2, and use vetted libraries rather than inventing your own. Keep keys in a secrets manager, not in code.
A03: Injection
This is the one to understand deeply, because the pattern repeats everywhere. Injection happens when user-supplied data is mixed into a command that an interpreter then runs, and the data is allowed to change the structure of that command rather than being treated as plain values. SQL injection is the classic case, but the same idea applies to operating-system commands, LDAP queries and more.
Why it happens: the code builds a query by gluing strings together, so input that contains query syntax becomes part of the query:
# Vulnerable: the user's input becomes part of the SQL structure
query = "SELECT * FROM users WHERE email = '" + user_input + "'"
cursor.execute(query)
If user_input contains SQL syntax, it changes what the query does. The database cannot tell the difference between the developer's intent and the attacker's addition, because they arrive as one string.
How to fix it: never build queries by concatenation. Use parameterised queries (also called prepared statements), which send the query structure and the values separately, so the database always treats input as data:
# Safe: the value can never change the query's structure
query = "SELECT * FROM users WHERE email = %s"
cursor.execute(query, (user_input,))
Every mainstream language and ORM supports this. The rule is simple and absolute: user input is a value, never part of the query text. The same principle defends against OS command injection (use library calls with argument arrays instead of building shell strings) and other injection types.
A04: Insecure design
What it is: the flaw is in the design, not a single line of code. For example, a password reset flow that reveals whether an email address is registered.
Why it happens: security was not considered when the feature was designed, so no amount of careful coding can fully save it.
How to fix it: threat-model features before building them. Ask, for each new feature, "how could this be abused?" and design the answer in.
A05: Security misconfiguration
What it is: default passwords left in place, debug mode on in production, verbose error pages, unnecessary features enabled, permissive cloud storage.
Why it happens: systems ship with convenient defaults, and hardening is skipped under deadline pressure.
How to fix it: define a hardened baseline, automate it with infrastructure as code, and turn off debug output and directory listing in production. A page that shows a full stack trace on error is a misconfiguration that hands attackers a map.
A06: Vulnerable and outdated components
What it is: using libraries, frameworks or runtimes with known vulnerabilities.
Why it happens: dependencies are added and never updated; nobody tracks which versions are in use.
How to fix it: keep a software bill of materials, run dependency scanning (such as Snyk or Dependabot) in your pipeline, and patch on a schedule. This is the finding that appears in nearly every report.
A07: Identification and authentication failures
What it is: weak login systems: no protection against password guessing, weak session handling, no multi-factor option.
How to fix it: rate-limit and lock out repeated failures, offer multi-factor authentication, use a well-tested session framework, and never roll your own login from scratch.
A08: Software and data integrity failures
What it is: trusting code or data that could have been tampered with, such as an unsigned update or a build pipeline that pulls dependencies without verification.
How to fix it: verify signatures, pin dependencies, and secure your CI/CD pipeline, because a compromised build process ships malicious code to every user at once.
A09: Security logging and monitoring failures
What it is: attacks that go unnoticed because nothing was logged or nobody was watching.
How to fix it: log authentication events, access-control failures and server errors, centralise the logs, and alert on the patterns that matter. In India, note that CERT-In directions require certain logs to be retained and incidents reported within tight timelines.
A10: Server-side request forgery (SSRF)
What it is: the server can be tricked into making requests to places it should not, such as internal services or cloud metadata endpoints.
How to fix it: validate and restrict the destinations your server is allowed to call, and never let user input control an internal request unchecked.
Cross-site scripting, the other one to understand
Cross-site scripting (XSS) did not get its own top-level slot in the latest revision (it now sits under injection), but freshers still meet it constantly, so it is worth its own explanation. XSS is injection aimed at the browser instead of the database.
Why it happens: the application takes data from a user and places it into an HTML page without encoding it, so text that contains HTML or script markup is interpreted by the browser as code rather than displayed as content. If a comment field stores whatever you type and the page later renders it raw, markup in that comment runs in every visitor's browser.
How to fix it, in three layers:
- Output encoding. When you place any user data into a page, encode it for the context (HTML body, attribute, JavaScript, URL). Modern frameworks such as React and Angular do this by default, which is one reason to use them rather than building HTML strings by hand.
- Avoid dangerous sinks. Do not pass user data into constructs that treat strings as code, such as
innerHTMLordangerouslySetInnerHTML, without sanitising it first with a vetted library like DOMPurify. - Content Security Policy. Add a CSP header as a safety net, so that even if something slips through, the browser refuses to run unauthorised scripts:
Content-Security-Policy: default-src 'self'; script-src 'self'
Notice the shared pattern with SQL injection: data crossing a trust boundary must be treated as data, and encoded for wherever it is going. Learn that once and eight of the ten categories become variations on a theme.
How this shows up in a VAPT report
In a real penetration test, findings are mapped to these categories and scored with CVSS so that a business can prioritise. A broken access control that exposes other customers' data will rank far above a missing security header. As a fresher, learning to explain why a bug is dangerous and how to fix it, in the developer's language, is what turns a scanner result into advice a team will actually act on.
What to do this week
- Set up OWASP Juice Shop locally and read its official companion guide as you explore each category.
- Work through the free PortSwigger Web Security Academy labs for access control and injection.
- In your own code, find one query built by string concatenation and convert it to a parameterised query.
- Add a Content-Security-Policy header to a personal project and watch what it blocks.
Understand the ten categories and the one idea behind them, and you have the foundation that everything else in application security builds on.
- ethical-hacking
- freshers
- owasp
- appsec
- secure-coding
- vapt