A cryptographer is a scientist who invents and analyzes cryptographic algorithms, while a security engineer is a practitioner who uses those algorithms and other tools to build and maintain secure systems.

Let’s see what a security engineer actually does when faced with a real-world problem. Imagine we’re building a simple web application that needs to store user credentials.

POST /users HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "username": "alice",
  "password": "supersecretpassword123"
}

When this request comes in, a security engineer’s mind immediately goes to how to protect that password. They don’t invent new hashing algorithms (that’s the cryptographer’s job). Instead, they select and implement existing, well-vetted cryptographic primitives.

Here’s a simplified look at what happens on the server-side, orchestrated by the security engineer:

  1. Receive Credentials: The raw username and password arrive.

  2. Hashing: The password isn’t stored directly. It’s passed through a one-way cryptographic hash function, typically something like Argon2, scrypt, or bcrypt. These algorithms are designed to be computationally expensive, making brute-force attacks much harder. A security engineer would configure the parameters for this hashing. For instance, with bcrypt, they might set a "cost factor" (work factor). A common value is 12.

    import bcrypt
    
    password_bytes = b"supersecretpassword123"
    # Generate a salt and hash the password
    hashed_password = bcrypt.hashpw(password_bytes, bcrypt.gensalt(rounds=12))
    print(hashed_password)
    # Example output: b'$2b$12$aBcDeFgHiJkLmNoPqRsTu.vWxYz1234567890ABCDEFGH'
    
  3. Salting: Crucially, a unique, random "salt" is generated for each password before hashing. This salt is stored alongside the hash. The purpose of salting is to ensure that even if two users have the same password, their stored hashes will be different. This defeats precomputed rainbow tables. The bcrypt.gensalt() function handles this automatically.

  4. Storage: The username and the salt-and-hash combination are stored in the database. The original password is never stored.

    -- Example database entry for alice
    INSERT INTO users (username, password_hash)
    VALUES ('alice', '$2b$12$aBcDeFgHiJkLmNoPqRsTu.vWxYz1234567890ABCDEFGH');
    
  5. Authentication: When Alice tries to log in, she sends her username and password. The system retrieves her stored hash and salt. It then re-hashes her provided password using the stored salt and the same hashing algorithm (bcrypt with rounds=12). If the newly generated hash matches the stored hash, authentication succeeds.

    import bcrypt
    
    provided_password_bytes = b"supersecretpassword123"
    stored_hash_and_salt = b'$2b$12$aBcDeFgHiJkLmNoPqRsTu.vWxYz1234567890ABCDEFGH'
    
    if bcrypt.checkpw(provided_password_bytes, stored_hash_and_salt):
        print("Authentication successful!")
    else:
        print("Authentication failed.")
    

The security engineer’s role is to choose the right algorithms (like Argon2 over MD5), configure them with appropriate parameters (e.g., rounds=12 for bcrypt, or specific memory/time costs for Argon2), and ensure they are applied correctly at every step (hashing, salting, verification). They are the builders and guardians. The cryptographer is the one who proved that bcrypt, when configured correctly, is resistant to certain types of attacks, and they might be working on the next generation of even stronger algorithms.

A security engineer doesn’t just handle passwords. They also consider things like:

  • Transport Security: Using TLS/SSL (HTTPS) to encrypt data in transit, preventing eavesdropping. This involves understanding certificate authorities, cipher suites, and TLS versions.
  • Access Control: Implementing role-based access control (RBAC) or attribute-based access control (ABAC) to ensure users only access what they’re authorized to.
  • Input Validation: Sanitizing user inputs to prevent injection attacks like SQL injection or Cross-Site Scripting (XSS).
  • Auditing and Monitoring: Setting up logging to track security-relevant events and using tools to detect suspicious activity.
  • Vulnerability Management: Regularly scanning systems for known vulnerabilities and applying patches.

The fundamental difference lies in the level of abstraction and focus. Cryptographers are deep in the mathematical theory and design of cryptographic primitives. Security engineers are applying these primitives, along with a broad range of other techniques and best practices, to solve practical security problems in software and infrastructure.

What most people don’t realize is that the "strength" of a password hashing scheme isn’t just in the algorithm itself, but in how it’s tuned. An algorithm like Argon2 has multiple tunable parameters: memory cost, time cost (iterations), and parallelism. A security engineer must balance security against performance, choosing values that make brute-forcing infeasible on current hardware while still allowing legitimate logins to be processed quickly enough for a good user experience. A common Argon2id configuration might look like Argon2(memory_cost=102400, time_cost=8, parallelism=1).

Once you’ve got password hashing sorted, the next immediate security concern is usually preventing unauthorized access to the API endpoints themselves.

Want structured learning?

Take the full Cryptography course →