• Level Up Coding
  • Posts
  • LUC #85: Session Management Demystified: Cookies, Tokens, and Security

LUC #85: Session Management Demystified: Cookies, Tokens, and Security

Plus, load balancer vs reverse proxy, how to manage protobuf APIs, and 4 database scaling strategies you should know

This week’s issue brings you:

READ TIME: 5 MINUTES

Thanks to our partners who keep this newsletter free to the reader.

Your terminal, but smarter

Q Developer CLI provides a lightning-fast agentic coding experience. Generate code on the fly? Read and write files locally? MCP support? Effortlessly call APIs? Claude 3.7? Run bash commands?

Session Management Demystified: Cookies, Tokens, and Security

Imagine having to reintroduce yourself every time you speak to someone.

Exhausting, right?

Our applications experience the same dilemma. They need a way to recognize the users interacting with them.

This is where session management comes in.

Session management is the practice of maintaining a persistent, authenticated state for users interacting with web applications. It ensures that the server can identify and authenticate users across multiple requests, ensuring that interactions can flow without constant reintroductions.

Why is it Important?

There are two primary reasons that session management is a fundamental component of web applications; maintaining user state, and security.

HTTP is a stateless protocol, meaning each request from a client to a server is treated as a new, standalone request with no memory of previous interactions.

Session management provides a way to retain user-specific data across multiple requests.

As for security, proper session management ensures that the user is who they say they are throughout the session's lifespan, providing protection against unauthorized access, CSRF attacks, session hijacking, and more.

The Mechanics Behind the Scenes

Not all session management strategies are created equal. Here are three widely used approaches, each with its own strengths and trade-offs:

Cookies

Cookies are small pieces of data sent from a server and stored on a user's web browser while the user is browsing. They are the most common session management tool, and can be used for both server-side session IDs and stateless tokens like JWTs.

Upon a successful login, the server sends a unique session ID in a cookie. With each subsequent request, the browser sends this cookie, reminding the server that they've met before.

Token-Based Management (Stateless)

Modern, distributed applications often rely on token-based systems, which eliminate the need for server-side session storage.

How it works: After login, the server issues a JSON Web Token (JWT) or similar token, which the client stores in local storage, session storage, or a secure, HTTP-only, Secure cookie.

Why use it: Tokens are stateless, meaning the server doesn’t need to maintain a session store. This approach scales well for microservices, APIs, and multi-region architectures, but comes with its own security considerations, like token expiration and revocation.

Advanced considerations: Implement token rotation to reduce replay attack risks and consider token binding to cryptographically tie tokens to specific clients.

Real-world use: Widely adopted in Single Page Applications (SPAs), microservices, and serverless architectures where scalability and distributed data flow are critical.

Server-Side Session Management (Stateful)

This traditional approach keeps the session state on the server, typically in a database, in-memory store (eg; Redis), or file system.

How it works: The client holds only a session identifier, while the server manages the full session state. This can offer tighter control over session data but introduces scalability challenges, as session data must be synchronized across multiple servers.

Scalability: To handle scale, use sticky sessions, distributed caches, or centralized session stores like Redis or Memcached.

Real-world use: Often found in legacy applications or scenarios where strong consistency is a priority, like financial systems or enterprise portals.

Core Security Best Practices

1) Session expiry and invalidation:

— Set reasonable expiration times to limit the risk of session hijacking.
— Invalidate sessions on logout, password change, or account deletion.
— Use absolute and sliding expiration policies depending on your risk tolerance.

2) Session ID regeneration (session fixation protection):

— Regenerate session IDs upon login or privilege escalation to prevent session fixation attacks.
— Use randomized, cryptographically secure session IDs.

3) Secure cookie attributes (if using cookies):

— Secure: Prevents transmission over non-HTTPS connections.
— HttpOnly: Prevents client-side JavaScript from accessing the cookie.
— SameSite: Restricts cross-site request contexts to mitigate CSRF.

4) Token rotation and revocation (if using tokens):

— Use short-lived tokens with automatic rotation to reduce the impact of token theft.
— Implement token revocation mechanisms for high-risk actions.
— Use the OAuth 2.0 standard where possible for fine-grained access control.

5) Least privilege and scoping:

— Limit the scope of session tokens to the minimum necessary for the intended actions. — Use fine-grained access controls and avoid over-scoping JWTs.

6) Zero trust and device binding:

— Consider token binding to cryptographically tie tokens to specific devices.
— Use mutual TLS (mTLS) for higher security in sensitive applications.

Final Thoughts

Session management, though often operating in the background, stands as a cornerstone in ensuring the fluidity and security of our applications and systems. Proper session management not only plays a role in creating smooth user experiences. In an era marked by cyber threats, it’s a major pillar of keeping our applications and systems secure.

Load Balancer vs Reverse Proxy — What’s The Difference (Recap)

Load balancers are concerned with routing client requests across multiple servers to distribute load and prevent bottlenecks. This helps maximize throughput, reduce response time, and optimize resource use.

reverse proxy is a server that sits between external clients and internal applications. While reverse proxies can distribute load as a load balancer would, they provide advanced features like SSL termination, caching, and security. Reverse proxies are more concerned with limiting and safeguarding server access.

Whilst load balancers and reverse proxies possess distinct functionalities, in practice the lines can blur, as many tools act as both a load balancer and reverse proxy.

How to Manage Protobuf APIs (Recap)

Managing Protobuf APIs can be challenging at scale. Most teams rely on manually shared .proto files and custom scripts for validation. This approach often leads to coordination issues and operational risks as APIs expand across services.

Why a Protobuf Schema Registry Matters:

• Centralized management: The Buf Schema Registry (BSR) offers a single source of truth, streamlining API governance and reducing manual overhead.

• Policy enforcement: Automates policy rules to prevent breaking changes, ensuring API stability.

• Developer experience: Provides auto-generated SDKs, hosted plugins, and modern tools like a CLI and UI for a cleaner workflow.

• Seamless integration: Compatible with Kafka and Confluent SR APIs for easy integration with your existing ecosystem.

The Buf Schema Registry transforms Protobuf management from a risky manual process into a streamlined, automated, and governance-focused approach, making it a powerful tool for scaling APIs effectively.

4 Database Scaling Strategies You Should Know (Recap)

Before scaling your database, remember to avoid premature optimization, as it adds complexity, slows feature development, and complicates testing and bug resolution. Only scale when genuinely needed. Here are four widely used scaling approaches:

1) Caching database queries — Reduce database load by storing frequently requested data in memory (eg; Redis, Memcached) to avoid repeated database hits.

2) Database indexes  Speed up data retrieval using indexes (eg; B-trees), which reduce search time from O(n) to O(log n) by allowing quick data location without full table scans.

3) Database read replication — In read-heavy environments, replicate the main database to distribute read traffic across multiple servers, improving read performance and reliability.

4) Database sharding — Distribute data across multiple servers by splitting it into independent pieces (shards). This supports horizontal scaling but introduces significant complexity.

While these are some of the most popular database scaling strategies, many others exist, including partitioning, connection pooling, and more.

That wraps up this week’s issue of Level Up Coding’s newsletter!

Join us again next week, where we’ll explore and visually distil more important engineering concepts.