Scala
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Object-Oriented Scala
-
Section 4: Functional Scala
-
Section 5: Collections in Depth
-
Section 6: Type System
-
Section 7: Concurrency and Ecosystem
-
Section 8: Practical Projects
-
Section 9: Interview Practice
-
Section 10: Data Structures and Algorithms in Scala
-
Section 11: More Practice Exercises
-
Section 12: Advanced Functional Patterns
-
Section 13: More Ecosystem
-
Section 14: Scala Collections Library Deep Dive
-
Section 15: Scala Standard Library Deep Dive
-
Section 16: Akka Ecosystem Deep Dive
-
Section 17: Cats and Cats Effect Deep Dive
-
Section 18: Play Framework Deep Dive
-
Section 19: Apache Spark with Scala Deep Dive
-
Section 20: Scala Build Tools Deep Dive
-
Section 21: Scala 3 Specific Features
-
90: Union and Intersection Types
-
Section 22: Scala Testing Deep Dive
-
Section 23: Functional Domain Modeling
-
Section 24: More Data Structures and Algorithms in Scala
-
Section 25: Scala for Data Engineering
-
Section 26: More Practical Projects
-
Section 27: More Interview and Review
-
Section 28: ZIO Ecosystem Deep Dive
-
Section 29: Scala for Machine Learning
-
Section 30: Scala Microservices Architecture
-
Section 31: Scala Type System Deep Dive
-
Section 32: More Practice and Drills
-
Section 33: Scala Performance Deep Dive
-
Section 34: Scala Ecosystem Tooling
-
Section 35: Scala for Reactive Systems
-
Section 36: More Real-World Case Studies
-
Section 37: Scala for Financial Systems
-
Section 38: Scala GraphQL and gRPC
-
Section 39: More Final Projects
-
Section 40: More Interview and Final Review
-
Section 41: Scala for Streaming Data
-
Section 42: Scala Security Practices
-
Section 43: More Language Deep Dive
-
Section 44: Scala Command-Line Tools
-
Section 45: Scala Documentation and Style
-
Section 46: Scala Dependency Management
-
Section 47: More Practical Backend Patterns
-
Section 48: Scala for Event-Driven Architecture
-
Section 49: More Practice Drills Round 2
-
Section 50: Scala Compiler Deep Dive
-
Section 51: Scala for Web Frontends
-
Section 52: More Data Engineering Practice
-
Section 53: Scala Observability
-
Section 54: More Advanced Practice Projects
-
Section 55: Scala for Legacy Java Integration
-
Section 56: More Testing Practice
-
Section 57: Final Mastery Review
-
Section 58: Scala History and Ecosystem Context
-
Section 59: More Concurrency Patterns
-
Section 60: Scala for Configuration Management
-
Section 61: More Domain Modeling Practice
171: Secure Coding Practices in Scala
I've seen a lot of developers move to Scala and feel a sudden sense of confidence because the type system is so powerful. But here's the thing: a strong type system doesn't automatically make your code secure. In fact, some of the most common security vulnerabilities in Scala apps happen because we rely on String for everything. I call this "Stringly-typed" programming, and it's a recipe for disaster when you're handling secrets or database queries.
The danger of the generic String
Let's look at a naive way of handling a user profile. You might be tempted to just throw everything into a case class using String for the username, the email, and the API key. It's fast to write, and it works perfectly during the first demo.
case class UserProfile(username: String, email: String, apiKey: String)
val user = UserProfile("jdoe", "jane@example.com", "sk_live_51MzX12345")
println(s"Processing request for user: $user")
The problem here isn't the logic; it's the toString method that case classes generate for you. The moment you log that user object for debugging—which happens more often than we'd like to admit—your customer's live API key is now sitting in plain text in your logging aggregator. I've seen production logs leaked in breaches where the "secret" was just a field in a case class that got printed.
Wrapping secrets in Opaque Types
A better way is to ensure that sensitive data cannot be accidentally treated as a normal string. In Scala 3, we can use opaque type to create a type that is a String at runtime but is treated as a distinct type by the compiler. This allows us to control exactly how that value is accessed and prevents it from being printed by default.
object Security {
opaque type Secret = String
def apply(value: String): Secret = value
// We explicitly define how to handle the secret,
// or simply don't provide a way to turn it back into a String easily.
def reveal(s: Secret): String = s
}
import Security._
case class UserProfile(username: String, email: String, apiKey: Secret)
val user = UserProfile("jdoe", "jane@example.com__(Secret "sk_live_51MzX12345"))
println(s"Processing request for user: $user")
// Now it prints: UserProfile(jdoe, jane@example.com, Secret "...")
// though you can customize the opaque type's representation further.
By doing this, you're forcing yourself (and your teammates) to be intentional. You can't accidentally pass a Secret into a function that expects a String, like a logger or a UI component, without explicitly calling a "reveal" method. It adds a tiny bit of friction, but that friction is exactly what saves you from a security audit nightmare.
When interpolation becomes a liability
Now, let's talk about the database. Scala's string interpolation is beautiful, but using it to build queries is the fastest way to introduce SQL injection. I've seen people do this because it "looks" like a template, but it's actually just concatenating strings under the hood.
// The naive, dangerous way
def findUser(username: String): String = {
s"SELECT * FROM users WHERE username = '$username'"
}
If a user passes ' OR '1'='1 as their username, they've just bypassed your authentication. You might think, "I'll just sanitize the input," but sanitization is a losing game. There's always an edge case you'll miss.
The professional approach is to use parameterized queries. Whether you're using a raw JDBC driver or a high-level library like Doobie or Quill, you should never manually interpolate values into a query string. Instead, you use placeholders. The database driver then handles the value separately from the command, making it mathematically impossible for the input to be executed as code.
// The secure way (using a hypothetical DB library)
def findUser(username: String) = {
sql"SELECT * FROM users WHERE username = $username".query[User].unique
}
In the example above (which looks like Quill), the sql"..." interpolator isn't actually doing string concatenation. It's building an Abstract Syntax Tree (AST) and sending the username as a bound parameter. This is a critical distinction: one is a string operation, the other is a structural operation. Always opt for the latter when dealing with external input.
📋 Practical Task
Refactor the Vulnerable Account Manager
You have been handed a legacy piece of code for an AccountManager. It currently has two major security flaws: it leaks a session token in the logs and it is vulnerable to SQL injection via the lookupAccount method.
Your task:
- Create an
opaque typecalledSessionTokento wrap the token string, preventing it from being printed directly. - Refactor the
lookupAccountmethod to use a parameterized approach (simulate this by changing thes"..."interpolation to a mockdb.execute(query, params)call). - Ensure that the
Accountcase class uses theSessionTokentype instead ofString.
// VULNERABLE CODE TO FIX
case class Account(id: Int, token: String)
object AccountManager {
def lookupAccount(userInputId: String): String = {
// VULNERABLE: SQL Injection
s"SELECT * FROM accounts WHERE id = $userInputId"
}
def logAccount(acc: Account): Unit = {
// VULNERABLE: Leaks token to logs
println(s"Logging account access: $acc")
}
}There are no comments for now.