What's the worst someone could do with your HR system?

Pretend this is your company's HR portal. A developer wrote the login in twenty minutes and moved on. For the next five minutes, find out what that decision actually cost. Everything runs in your browser — no server, no stakes, no data leaves this page.

Parable HR Portal

Locked

Parable Industries

Employee Portal

Need a push? Reveal hints one at a time.

Try typing a single quote ' in the username field. What happens to the SQL query?
SQL uses AND and OR to combine conditions. What if you could make the WHERE clause always true?
Try this username: ' OR '1'='1' -- with any password. The -- comments out the rest of the query.
UNION needs matching column counts. SELECT * on the login query returns 8 columns. To see the table structure, pull from SQLite's metadata table. Username: ' UNION SELECT name, sql, NULL, NULL, NULL, NULL, NULL, NULL FROM sqlite_master --. Any password. The schema panel on the right will unlock.
Now that you've seen the schema, craft a UNION with the exact column list. Username: ' UNION SELECT id, username, password, name, role, salary, pip, notes FROM employees --. Any password. You'll dump every salary, PIP flag, and private note.

Query Inspector

See exactly what the app executes
sqlite — hr_portal.db
-- Loading in-browser SQLite engine...

Table schema

Locked

Schema hidden. Dump it with a UNION against sqlite_master (Hint 4) to unlock this panel.

What just happened

The app built its SQL query by pasting your input directly into the query string using string concatenation. The template looks like this:

SELECT * FROM employees WHERE username='[INPUT]' AND password='[INPUT]'

What your input produced this time:

 

When the ' in your input closed the string early, the rest became code, not data. OR '1'='1' made the WHERE clause always true; -- commented out the password check. The database returned everything it could see.

What this would actually cost you

You just pulled every salary in the company, plus medical-leave reasons and termination plans. If this happened for real, the GDPR fine would be the least of it. Every employee whose salary leaked is a potential EEOC complaint. The engineer whose rehab status is now public has a wrongful-disclosure case. And pay transparency, when it arrives by leak instead of policy, is a resignation event — plan for ten to twenty percent attrition in the quarter. One line of code above cost you all of that.

The fix is straightforward. Never build SQL by concatenation. Use parameterized queries so user input is always treated as data.

The fix: parameterized queries

Every modern database driver supports parameterized queries. Instead of pasting user input into the SQL string, you send the query and the values separately. The driver treats values as data, not code, so ' OR '1'='1' -- becomes a literal username, not a logic operator.

Python (sqlite3 / psycopg2)

# Bad — string concatenation
query = f"SELECT * FROM users WHERE username='{username}' AND password='{password}'"
cursor.execute(query)

# Good — parameterized
cursor.execute(
    "SELECT * FROM users WHERE username=? AND password=?",
    (username, password)
)

Node.js (pg, mysql2, better-sqlite3)

// Bad
const rows = await db.query(`SELECT * FROM users WHERE username='${username}'`)

// Good
const rows = await db.query(
  'SELECT * FROM users WHERE username = $1',
  [username]
)

Parameterized queries are the minimum defense. Input validation, least-privilege database accounts, and a Web Application Firewall all add depth, but nothing substitutes for never concatenating user input into SQL in the first place.

Your own apps don't have this, right?

Knowing is different from hoping. A SignumCyber assessment checks your actual applications for this exact class of bug — and the 99 other ones we don't have labs for yet.

Talk to an advisor

Your apps don't have this bug. Probably.