Understanding JWT Claims and Best Practices for Secure Token Authentication
Learn how JWT claims work, explore registered, public, and private claims, and discover security best practices for implementing JSON Web Tokens in your applications.
SQL injection is still the #1 web attack. Learn the real difference between escaping and parameterized queries — with code examples in Python, PHP, and Node.js — and why one method leaves you exposed.
I've seen SQL injection destroy projects. About six years ago, I inherited a codebase at Šikulovi s.r.o. where someone had built a custom CMS with zero parameterization. Just raw string concatenation everywhere. Within two months of launch, the client's entire user database got dumped. Passwords, emails, addresses - everything. That experience changed how I approach database security forever.
There are two ways to prevent SQL injection: escaping and parameterization. Both can work, but after cleaning up that mess, I have very strong opinions about which one you should use. Spoiler: it's parameterization, always.
If you don't understand how SQL injection works, you can't defend against it properly. The core problem is mixing code (your SQL) with data (user input) in the same string.
Escaping transforms dangerous characters so they're treated as literal text, not SQL syntax. Single quotes get backslashed, that sort of thing. Sounds reasonable, right?
I don't use escaping for SQL injection prevention anymore. Here's why it's a ticking time bomb:
Parameterized queries (or prepared statements - same thing) completely separate your SQL code from your data. You write your query with placeholders, then bind the data separately. The database driver handles escaping internally, correctly, every time.
Every project at Šikulovi s.r.o. uses parameterized queries. Period. Here's why it's not even a debate:
I use PDO for everything in PHP. It's database-agnostic and the API is clean. Here's what it looks like in practice:
// PDO with positional parameters
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$userId]);
// MySQLi
$stmt = $mysqli->prepare("SELECT * FROM users WHERE id = ?");
$stmt->bind_param("i", $userId);
// Named parameters (my preference)
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(["id" => $userId]);Every decent Node.js database library supports parameterization. The syntax varies a bit, but the concept is the same.
// mysql2
connection.execute("SELECT * FROM users WHERE id = ?", [userId])
// pg (PostgreSQL)
client.query("SELECT * FROM users WHERE id = $1", [userId])
// better-sqlite3
db.prepare("SELECT * FROM users WHERE id = ?").get(userId)
// Sequelize ORM
User.findOne({ where: { id: userId } })
// Prisma ORM
prisma.user.findUnique({ where: { id: userId } })Python's DB-API 2.0 spec standardizes this across libraries. The %s placeholder threw me off at first - it's NOT string formatting.
# psycopg2
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
# mysql-connector
cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,))
# sqlite3
cursor.execute("SELECT * FROM users WHERE id = ?", (user_id,))
# SQLAlchemy
session.execute(text("SELECT * FROM users WHERE id = :id"), {"id": user_id})
# Django ORM
User.objects.get(id=user_id)Java's JDBC has solid PreparedStatement support. Most frameworks build on top of this.
// JDBC PreparedStatement
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setInt(1, userId);
// JPA/Hibernate
entityManager.createQuery("SELECT u FROM User u WHERE u.id = :id")
.setParameter("id", userId)
// Spring Data JPA
userRepository.findById(userId)
// jOOQ
dsl.selectFrom(USERS).where(USERS.ID.eq(userId))Here's something that trips people up: you can only parameterize DATA values, not structural SQL elements. This requires different handling.
When I need dynamic table names, column names, or SQL keywords, I use allowlists. Validate against known-good values, reject everything else.
Even with parameterized queries, people make mistakes. I see these constantly:
IN clauses are tricky. You need as many placeholders as you have values. Here's how I handle them:
ORMs use parameterization internally, so they're generally safe. But you can still shoot yourself in the foot.
I've had clients tell me "we use stored procedures so we're safe from SQL injection." That's not how it works.
Parameterization should be your primary defense. But I never rely on just one layer. Security is about multiple barriers.
Trust but verify. Every project at Šikulovi s.r.o. gets SQL injection testing.
Got a codebase full of mysqli_real_escape_string? I've been there. Here's how I migrate:
The escaping vs parameterization debate is over. Parameterization wins. It's more reliable, more maintainable, and every security organization recommends it. I don't even consider escaping for new projects anymore.
If you're still using escaping, start migrating. If you're building something new, use parameterized queries from day one. Future you (and your users' data) will thank you.
Escaping is a legacy technique that can work if implemented perfectly, but it is error-prone and not recommended. Parameterized queries are simpler, more reliable, and the industry standard. Use parameterization for all new code.
ORMs generally protect against SQL injection when used correctly because they use parameterization internally. However, raw query features can be vulnerable if you concatenate user input. Always use the ORM parameterization syntax for raw queries.
Yes. Even if you expect a number, an attacker can send "5 OR 1=1" which becomes valid SQL when concatenated. Always use parameterization regardless of expected data type. Type-safe parameter binding provides additional protection.
Use a parameter for the entire pattern including wildcards. For example: "WHERE name LIKE ?" with parameter "%" + searchTerm + "%". Escape percent and underscore characters in the search term if you want them treated literally.
SQL parameters can only represent data values, not structural elements like table or column names. The query structure must be fixed when the statement is prepared. Use allowlists to validate dynamic identifiers against known-good values.
The terms are often used interchangeably. Technically, a prepared statement is the database-side compiled query, while parameterization refers to the technique of binding values separately. In practice, both mean separating SQL code from data values.
Parameterization often improves performance. The database can cache and reuse the prepared statement execution plan. For queries executed many times with different values, prepared statements are faster than parsing each query individually.
Try entering special characters like single quotes, double quotes, and SQL keywords in input fields. Use automated tools like SQLMap or Burp Suite. Perform code review looking for string concatenation in queries. Consider professional penetration testing for critical applications.
Founder of CodeUtil. Web developer building tools I actually use. When I'm not coding, I experiment with productivity techniques (with mixed success).
Learn how JWT claims work, explore registered, public, and private claims, and discover security best practices for implementing JSON Web Tokens in your applications.
Learn why password hashing matters, why MD5 is broken for security, how SHA-256 differs, and why bcrypt and Argon2 are the right choice for storing passwords. Understand rainbow tables, salting, and modern best practices.
Understand JSON Web Tokens (JWT) from the ground up: how they work, their three-part structure, when to use them, security best practices, refresh token strategies, and common implementation mistakes to avoid.