Picking up from last lesson — examples and hands-on challenges throughout
Last lesson we covered SQL injection, XSS, CSRF, and started APIs. Before diving in, let's make sure those three attacks are locked in. Click each card to check your understanding.
How does it work? What stops it?
Click to reveal
admin'-- comments out the password check).What does it target? One-line fix?
Click to reveal
innerHTML.textContent β the browser treats the value as plain text, not parseable HTML.
What does it exploit? How stopped?
Click to reveal
You know what an API is (a way for software to talk to external services) and why API keys exist (they authenticate you to the service and are tied to your account for billing). Today we cover what goes wrong when those keys end up exposed β and three more vulnerability classes to finish the picture.
A hardcoded secret is any sensitive value β API key, password, database credential, private token β written directly into source code. It's the fastest way to get something working, which is why it happens constantly. But source code travels everywhere: pushed to GitHub, shared with collaborators, deployed to servers, cached by build tools, sometimes accidentally made public.
Every place your code goes, your secret goes with it.
Each snippet below contains a hardcoded secret. Click on the part you think is the problem to reveal what's wrong and why it matters.
"sk-proj-abc123..." is hardcoded. Anyone who sees this file β through GitHub, a shared terminal, or a code review β can make API calls billed to your account. Automated bots typically exploit exposed keys within 4β7 minutes of a public commit. Developers have received five-figure cloud bills from a single exposure.
"admin2025". An attacker with the hash can run a dictionary attack or check a rainbow table. Even without the comment, the hash itself reveals which algorithm you're using and gives attackers a head start on cracking it.
If you commit a secret and then delete it in the next commit, the secret still exists in your git history forever. Anyone who clones the repository can run git show <hash> to retrieve the original file from any previous commit. Deleting is not enough β you must rotate the key immediately and treat it as compromised.
The correct pattern: read secrets from the environment at runtime. Environment variables are key-value pairs set outside your application β in a .env file locally, or via your hosting provider's dashboard in production. They never enter your code files, and therefore never enter your git repository.
import os
# The secret is read from the environment β never written here.
API_KEY = os.environ.get("OPENAI_API_KEY")
DB_PASSWORD = os.environ.get("DB_PASSWORD")
response = requests.get(
"https://api.openai.com/v1/chat/completions",
headers={"Authorization": f"Bearer {API_KEY}"}
)
# .env (local file only)
OPENAI_API_KEY=sk-proj-abc123def456ghi789jkl
DB_PASSWORD=VolcPanth#2025!
# This file is listed in .gitignore.
# On Vercel, you set these via Project Settings β Environment Variables.
# They are injected into the runtime environment when your app starts.
# .gitignore
.env
.env.local
.env.production
*.env
# These patterns tell git to completely ignore any .env files.
# They will never appear in git status, git add, or git log.
os.environ.get("SECRET_NAME") β only the name, never the value.env file with the values, always listed in .gitignoreSecurity through obscurity means relying on the secrecy of how your system works as the primary protection β not a strong password, not encryption, not server-side checks. Just the hope that attackers don't find out how it's built.
The moment someone discovers the design, all protection is gone. There is no fallback.
You've lived through an example of this. When Volcanic Pantheon had a JavaScript password check, making the GitHub repo private was obscurity: the protection depended on nobody finding the source code. But JavaScript always runs in the browser β any visitor can open DevTools β Sources and read every line of it, regardless of whether the repo is private.
Cryptographer Auguste Kerckhoffs stated what is still the gold standard of security design:
"A system should be secure even if everything about the system, except the key, is public knowledge."
In modern terms: security should still hold even if an attacker knows exactly how your code works. Only the secret value β a key, a password, a token β should be what keeps them out, not the design being hidden.
localStorage.setItem("authenticated", "true") β done.Click "Reveal verdict" on each scenario. Think about it first before clicking.
An admin panel sits at /xk92-admin-qp7 instead of /admin. There is no login β just knowing the URL grants access.
A server uses bcrypt password hashing. The source code is publicly available on GitHub.
A site requires a strong password AND the login form is at an unusual URL not linked from anywhere.
Blake makes the Volcanic Pantheon GitHub repo private. The site still checks passwords in JavaScript using localStorage.
Nobody writes everything from scratch. A package (also called a library or module) is code written by someone else that solves a specific problem β hashing passwords, making HTTP requests, parsing dates, handling file uploads. You install it and use it instead of building it yourself.
In Python: pip install requests. In JavaScript: npm install express. Your Vercel site for Volcanic Pantheon uses dozens of packages β Next.js alone pulls in hundreds of dependencies when installed.
Each package you install often depends on other packages, which depend on others still. This is the dependency tree. A project with 5 direct dependencies might pull in 200+ packages transitively. You are responsible for the security of every single one.
npm install express actually pulls inInsecure dependencies means using packages that contain known security vulnerabilities. Your own code might be flawless β but if any package in the tree has a flaw, your application inherits it. This is a supply chain attack: instead of attacking your code, the attacker compromises something your code relies on.
Log4j is a Java logging library used by hundreds of thousands of applications: Minecraft servers, banking systems, government portals, enterprise software.
In December 2021 a critical flaw was found: if Log4j logged a specially crafted string, it would execute arbitrary code on the server. An attacker could type something in a chat box, and if the server logged it, they could run anything they wanted remotely.
None of the developers who wrote those applications had done anything wrong. They installed a widely trusted library. The flaw was inside it.
Hundreds of organisations were compromised. The scramble to patch took weeks. CISA called it "one of the most serious vulnerabilities ever discovered."
npm audit scans your dependency tree against public vulnerability databases and reports what needs fixing. Run it regularly β especially before deploying.
npm audit regularly β especially before any deploymentEvery vulnerability we've covered targets software β a query, a DOM insertion, a cookie, a key in code. But even the most technically secure system can be bypassed if the people using it can be manipulated.
Social engineering exploits human psychology β trust, urgency, authority, helpfulness, fear β rather than code. No software patch fixes a user who hands their password to someone posing as IT support.
You've already experienced a real social engineering attack. When a friend saw your project name on your screen and searched for it, that was shoulder surfing β an attacker observing physical information you didn't intend to share. It caused you to rename the project, move to Vercel, and make the repo private. That's a genuine real-world social engineering incident affecting your own work.
| Attack | Mechanism | Real Example |
|---|---|---|
| Shoulder surfing | Watching someone's screen or keyboard to steal credentials or information | Friend sees project name on screen; searches for the site |
| Phishing | Fake emails or sites impersonating trusted entities to steal credentials | Fake GitHub security alert asking you to "verify" your account |
| Pretexting | Fabricating a scenario to extract information or access | "Hi, I'm from IT β we need your login urgently to fix an issue" |
| Baiting | Leaving infected USB drives or devices for victims to find | USB stick labelled "Salary Information 2025" in a company car park |
| Tailgating | Physically following an authorised person through a secured door | Walking in behind someone who badge-swiped through a server room door |
The email below has 5 red flags. Click the numbered circles in the email to jump to the explanation, or click the flags in the legend below to reveal each one.
Dear GitHub User,2
We have detected suspicious activity on your account. Your account will be permanently suspended within 24 hours3 unless you verify your identity immediately.
Please click the button below to confirm your account details:
Verify My Account Now4
You will be directed to: http://githb-verify.net/confirm5
β GitHub Security Team
github-security-alert.com, not github.com.
@github.com or @mail.github.com. Attackers register lookalike domains that look convincing at a glance. Always check the full domain, not just the display name β they're easy to fake.githb-verify.net β missing the 'u' in github.
githb vs github, paypai.com vs paypal.com). The fake site may look pixel-perfect but steals every credential entered. Always read the full domain carefully β attackers rely on you skimming past the URL.Deleting a file in a new commit does not remove it from version control history. Git retains every previous commit. An attacker who clones the repository can run git log followed by git show <hash> to retrieve the file from the commit where the key was present. To truly remove a secret from history, the developer must rewrite git history using tools such as BFG Repo-Cleaner β and even then, the key must be immediately rotated and treated as compromised, since anyone who cloned the repo before the rewrite still has the original history.
Security through obscurity means relying on the secrecy of a system's design as its primary protection, rather than on genuine security controls such as encryption or authentication.
Kerckhoffs's principle states that a system should be secure even if everything about it β except the key β is public knowledge. Security through obscurity violates this principle: if the design is the secret, discovering the design instantly removes all protection with no fallback.
Example: A JavaScript password check in a private GitHub repository. Making the repo private hides the source from GitHub, but the JavaScript still runs in every visitor's browser β opening DevTools always reveals it. Once the design is known, the check is bypassed immediately. A server-side bcrypt authentication would remain secure even with the source code fully public.
A supply chain attack targets software that an application depends on rather than the application's own code. The attacker compromises a library, package, or tool used by many developers, and the vulnerability propagates to all applications that depend on it.
Example: Log4Shell (CVE-2021-44228, 2021). Log4j is a widely used Java logging library. A critical flaw allowed remote code execution by logging a specially crafted string β attackers could trigger it by submitting input to any form or field that the application logged. Applications across banking, gaming, and government were vulnerable through no fault of their own code, simply because they used a trusted dependency.
Techniques: Phishing (fake emails or websites impersonating trusted entities to steal credentials) and pretexting (fabricating a scenario β e.g., posing as IT support β to manipulate a target into revealing information or granting access).
Shared defence: User education. Both attacks rely on the target not recognising that they are being manipulated. Training users to identify warning signs β unsolicited urgency, requests for credentials, mismatched domains, unexpected contact from authority figures β significantly reduces the success rate of both techniques. A user who knows these attacks exist is far harder to fool.
(Multi-factor authentication also applies to both: even if phishing or pretexting successfully captures a password, the attacker still requires the second factor.)
.env in .gitignore.npm audit, keep packages updated, minimise what you install.For each snippet: (a) name the vulnerability, (b) explain what an attacker could do with it, and (c) write the corrected version.
STRIPE_KEY = "sk_live_4eC39HqLyjWDarjtT7jkd782"
def process_payment(amount, card_token):
stripe.api_key = STRIPE_KEY
return stripe.Charge.create(amount=amount, source=card_token)
// Admin panel β no login required.
// The URL is the only protection.
app.get('/secret-admin-xk92', (req, res) => {
res.sendFile('admin.html');
});
You receive this SMS: "Your Vercel deployment failed due to a billing issue. Verify your card now to prevent your site going offline: vercel-billing-verify.net/confirm"
Red flags: (1) The URL is vercel-billing-verify.net β a lookalike domain, not vercel.com. (2) Urgency: threat of the site going offline. (3) Vercel communicates via email and the dashboard, not unsolicited SMS.
What an attacker could do: The fake site likely mimics the Vercel login or payment page. Entering credentials gives the attacker account access β they could deploy malicious code, modify environment variables to extract secrets, or access private repositories. Card details enable financial fraud.
What to do: Do not click the link. Open vercel.com directly in a new tab. Log in and check the billing and deployments sections. Any real issue will be visible there.