What you'll learn
Security isn't a feature you bolt on at the end — it's a mindset you code with from the start. This module covers the defensive fundamentals every Java developer must know to avoid the mistakes that lead to breaches. (This is defensive security: how to protect the software you build.)
By the end you'll be able to:
- Apply core secure-coding principles
- Validate input and prevent injection attacks
- Store passwords the right way (and know why)
- Understand hashing vs encryption and use secure randomness
- Handle secrets, dependencies, and deserialization safely
Secure-coding principles
A few mindsets prevent the majority of vulnerabilities:
- Never trust input — treat all external data (users, APIs, files) as potentially hostile
- Least privilege — give code, users, and services only the access they truly need
- Defense in depth — layer protections; don't rely on a single check
- Fail securely — on error, deny access rather than accidentally granting it
- Don't roll your own crypto — use vetted, standard libraries
Input validation & injection
The number-one source of breaches is trusting input. Injection attacks trick your program into treating attacker data as code. You saw SQL injection in Module 23 — the fix was parameterised PreparedStatements. The same principle applies everywhere: validate and separate data from commands.
The injection rule
../../ in a filename escapes your intended folder).Password storage
Passwords must never be stored in plaintext — if your database leaks, every account is compromised. Instead, store a one-way hash. But not just any hash: use a slow, salted algorithm built for passwords, so attackers can't brute-force them:
// ✗ NEVER store or compare plaintext passwords
// ✗ NEVER use a plain fast hash (MD5, or SHA-256 alone) for passwords
// ✓ Use a slow, salted, adaptive algorithm via a trusted library
// (BCrypt, Argon2, scrypt) — do NOT roll your own crypto.
String hash = BCrypt.hashpw(password, BCrypt.gensalt()); // at sign-up
boolean ok = BCrypt.checkpw(entered, hash); // at loginKey idea
Hashing vs encryption
These are often confused but serve different goals. Hashing is one-way (for passwords and integrity checks); encryption is two-way (for data you need to read back). Pick based on whether you ever need the original value:
Hashing — one-way
Turns data into a fixed digest you can't reverse. Verify by re-hashing and comparing. Use for passwords.
password → (salt + slow hash) → digest
Encryption — two-way
Scrambles data that a key can unscramble. Use to protect data you need to read back later.
data + key → cipher → data
For any randomness that protects something — session tokens, reset codes, keys — use SecureRandom, never java.util.Random:
import java.security.SecureRandom;
SecureRandom random = new SecureRandom(); // cryptographically strong
byte[] token = new byte[16];
random.nextBytes(token); // e.g. a session/reset token
// ✗ NEVER use java.util.Random for anything security-related —
// its output is predictable.Secrets & configuration
API keys, passwords, and tokens are secrets — they must never be hardcoded or committed to Git (Module 32). Keep them in environment variables, a config file that's git-ignored, or a dedicated secrets manager, and inject them at runtime:
// ✗ Hardcoded secret — ends up in Git history forever
String apiKey = "sk_live_9f8a7b6c5d";
// ✓ Read from the environment or a secrets manager
String apiKey2 = System.getenv("API_KEY");Other risks to know
- Deserialization — deserializing untrusted data (Module 19) can execute malicious code. Avoid Java serialization for external data; prefer JSON with strict parsing.
- Dependency vulnerabilities — libraries you use can have known flaws. Keep them updated and scan them (e.g. with OWASP Dependency-Check or GitHub Dependabot).
- Sensitive data in logs — never log passwords, tokens, or personal data (Module 30).
- Error messages — don't leak stack traces or internal details to end users.
Note
Recap & quick check
Key takeaways
- Never trust input; apply least privilege, defense in depth, and fail securely.
- Prevent injection by validating input and separating data from commands (parameterised APIs).
- Store passwords as slow, salted hashes (BCrypt/Argon2) — never plaintext, never MD5.
- Hashing is one-way (passwords); encryption is two-way (readable data). Use SecureRandom, not Random.
- Keep secrets out of code/Git, patch dependencies, avoid untrusted deserialization, and never log secrets.
Quick check
1. How should passwords be stored?
2. What is the best defense against SQL injection?
3. What's the difference between hashing and encryption?
4. Which class should generate security tokens?
5. Where should an API key be stored?
Excellent — you now build with security in mind. Next up: Module 37 — Advanced Architecture & Enterprise Concepts.