Common Types of Web Security
There are many types of common web security methods one can implement when creating a web app. Now disclaimer, I'm no security expert, but in this blog, I'll try my best to explain the 4 common types of web security that you may encounter when securing your applications: HTTP Basic Auth, API Keys/Opaque Tokens, JSON Web Token (JWT) and Session & Cookies, along with a bonus explanation of OAuth2 and OpenID Connect (OIDC).
HTTP Basic Auth
HTTP basic auth involves using a username and password that is sent on every HTTP request using the Authorization: Basic ... header. The username and password are Base64 encoded, which is less secure and different to encryption!
What do I mean by that? Well, encoding doesn't equal encryption. Encoding (Base64) is reversible by design so anyone who intercepts the request can decode it instantly. Encryption on the other hand, requires a secret key to reverse. This is why HTTPS should be mandatory with Basic Auth. HTTPS will encrypt the entire HTTP request, including the headers.
So the client would send a request to the server with a header like so:
Authorization: Basic dXNlcjpwYXNzd29yZA==
dXNlcjpwYXNzd29yZA== is actually username:password with Base64 encoding.
The server then decodes the header, compares the credentials to the user store (usually a database) and then either allows or denies the request based on the user store comparison.
Basic auth is well... basic. It's very easy to implement, supported everywhere and great for learning. However, due to being basic, the credentials go on every request and can be easily decoded without using HTTPS. The rotation of passwords and user management can also be harder to manage than other security methods. It's not a great option for public clients or scaling.
You would mainly use basic auth for use cases like: app/API demos, internal tooling or endpoints/services behind a VPN, reverse proxy, firewall, or IP allowlist.
Basic auth is secure if you:
- Enforce HTTPS
- Have strong credentials
- Limit access (internal tools, admin endpoints)
- Have few users that are easily controlled
It becomes insecure when:
- Used for public clients (mobile apps, SPAs)
- Passwords are reused
- You need frequent credential rotation
- Many users are involved
Remember that basic auth is operationally simple but not cryptographically fancy.
API Keys / Opaque Tokens
API keys or opaque tokens are random keys (or tokens) that are sent in the X-API-Key or Authorization: Bearer <token> header on an HTTP request. When the key is received by the server, the server will look up the token to ensure it is valid.
Opaque token means that the token has no meaning to the client, it's just a random string like so:
sk_live_9f83hf83hf8h3f
API keys work by the server generating a random token through some secure generation method. The random token is stored in a database, ideally hashed using a salt and server secret. The client will then send the token on every request like so:
Authorization: Bearer sk_live_9f83hf83hf8h3f
The server will look up the token in the database, checks the token validity, expiration and permissions (also stored in the database) and then maps the token to an identity.
This form of security is not as basic as basic auth. It's easier to rotate tokens, doesn't expose any passwords and can be issued per consumer of the service that requires authentication. However, it's more complex to perform identity claims without a manual lookup and the tokens are still a shared secret.
Identity claims are harder as the token itself doesn't have any information attached, so the server must query the database, load user/service identity and the load permissions. Shared secrets are more insecure as anyone with the API key has full access to the API. There is no proof of who is using the key. This makes it hard to: detect users abusing the API, prevent replay attacks and secure mobile or web frontend apps with potentially thousands/millions of users. If the API key does get leaked, you've got to rotate the key, immediately breaking all clients using the key.
You would mainly use API keys for service-to-service communication, cron jobs, CI/CD pipelines, scripting or limited partner integrations.
JWT (JSON Web Token) - The Modern Approach
JWT is one of the most common authentication approaches used across modern APIs. JWTs are a signed piece of JSON data that contains certain claims (user ID, roles, expiry) that prove who a user is, what they're allowed to do in an application (authentication + authorisation) and how long they have access for.
JWT security is a type of stateless authentication, meaning that the server (e.g. our API) does not need to store the session state for each logged-in user, the JWT token itself contains the information needed to authenticate the request. This means the server receiving the request will usually verify the JWT signature without using a database lookup. This is great for scaling, as your services don't need to share information about login sessions, instead they deal with JWTs that have all the information needed.
There's already great information on JWT tokens here. All we need to know for this blog is they usually look something like this:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6ImZ1bGxiZWFyZGVkZGV2IiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.3GPA9L5KydDdCTJXVCSTH5b-E2Iia2mV62MlzU-xUKg
The information is encoded and signed, not encrypted. This means anyone can decode the JWT token, so remember not to put any sensitive information in there. We can even decode this ourselves using the JWT.io debugger. If you did want some extra encryption or a signed JWT you could use a JWE or JWS, there's a great explanation on these types of JWTs and the rest of the JWT family here.
A typical JWT authentication flow would look something like this:
- A user logs in with a username and password on a website.
- Server for the website verifies the username and hashed password.
- The server generates a JWT token containing user info (user ID, role, expiry etc.).
- The client (e.g. the website on the browser) stores the token in local memory.
- Now when the user (client) requests something on the website, the token is sent in the request using the
Authorizationrequest header. - The server will receive the token, verify the signature, check the expiry, extract the claims and authorise the request.
- Now instead of logging in everytime, the user just sends the JWT token to make requests to the server.
No session stored on the server side, quick verifications and response times (depending on your server implementation ofc).
However, one major downside to JWT is difficulty to revoke credentials. As the JWT is stateless, the server doesn't have a stored session for the JWT and does not track active tokens, thus not having any control over removing malicious sessions. The token will live till its expiry.
The common way to manage this is having short-lived JWT access tokens, say with a 15 minutes expiry window, and then using a refresh token so the client can receive new access tokens without having to log back in.
JWT security is a great use case for single page applications (SPAs), mobile apps, distributed systems, microservices or scalable systems.
Session & Cookies
Using sessions and cookies usually involves a browser login that creates a sessions stored by the server and a matching session cookie stored in the browser.
What are sessions and cookies, well:
- A cookie is a small piece of data stored in the browser.
- A session is server-side stored data representing a logged-in user.
Now typically sessions and cookies would work together like so for auth:
- A user logs into a website with their username and password
- The server would validate the credentials
- Once validated the server would create some session data like so:
SESSION_ID → ID pointing to user data
e.g. SESSION_ID=5f8d2a4c-9f2d-4b4f-a53f-12ab34cd56ef
- The server sends back the session ID cookie:
Set-Cookie: SESSIONID=5f8d2a4c-9f2d-4b4f-a53f-12ab34cd56ef
- The browser will then store this session ID and automatically send the following on subsequent requests:
Cookie: SESSIONID=5f8d2a4c-9f2d-4b4f-a53f-12ab34cd56ef
- The server can then authenticate and validate requests based on the session ID. This session ID is now a pointer to a users session stored on the server.
Sessions allow web requests to become stateful, storing a users session info between requests. The cookies themselves are just storage containing information such as: Session IDs, theme preferences, language settings, shopping cart IDs, authentication tokens, analytics identifiers etc.
As for session IDs they point to user information stored on the server, they are usually not encrypted because they do not contain the user information. Instead, security comes from making them long, random, unguessable values and transmitting them securely over HTTPS.
Sessions fit naturally into classic web apps like older Spring MVC apps, server-rendered applications, PHP apps and traditional ecommerce sites. The user experience (UX) is also greatly improved, the user ultimately only has to log in once and that's it (unless they clear their cookies ofc). The browser in the background stores the session cookie and preserves the login state.
Some of these cookie properties can also help improve security:
Set-Cookie:
SESSIONID=5f8d2a4c-9f2d-4b4f-a53f-12ab34cd56ef;
HttpOnly;
Secure;
SameSite=Strict
- HttpOnly - will prevent JavaScript from reading the cookie, helping to prevent cross-site scripting (XSS) attacks.
- Secure - only sent over HTTPS, preventing the cookie being sent over plain HTTP.
- SameSite=Strict - Controls cross-site sending, helping to reduce cross-site request forgery (CSRF) attacks.
Having some sort of state stored on the server also means invalidating and revoking access to sessions is a lot easier to control. Also, the fact the session data is actually stored on the server, and the session ID is just a random ID, means the user details don't need to be transmitted over the network to the server.
However, like everything, there are some downsides to session and cookie auth. A main one being the server has to store state, every active user will need to be stored in-memory which could be millions depending on the system. Scaling can also be difficult, users may hit different servers behind a load balancer, meaning the session may not exist on some servers. There are solutions like sticky sessions and shared session stores, but this can increase the complexity. There's also a concern over CSRF as the browser automatically sends cookies. It's also less ideal for APIs, mobile apps don't naturally handle browser cookies, command line interface (CLI) tools also become awkward to use. Instead, APIs are usually better suited to using the Authorization header and tokens as it's consistent everywhere.
OAuth2 & OpenID Connect - Bonus!
Alongside the web security techniques discussed in this blog, OAuth2 and OpenID Connect are common in enterprise applications and modern web platforms. For a simple CRUD API they may be overkill, but as systems grow they become a common way to provide secure login and access.
OAuth2 is an authorisation framework that allows applications to access resources on a user’s behalf without sharing their password. Instead of handing credentials directly to an application, users will grant limited permissions through access tokens.
OpenID Connect (OIDC) builds on top of OAuth2 by adding authentication and identity. It allows applications to verify who a user is and provides user information through an ID token.
So in the real world, OAuth2 is the "What can this application access?", while OIDC is the "Who is the user?". These are then used together for an application login such as: "Continue with Google" or "Sign in with GitHub".
Web Security Comparison
Below is a comparison of all the web security methods mentioned in this blog:
| Security Method | Use Case | Pros | Cons |
|---|---|---|---|
| Basic Auth | Internal tools, admin endpoints, demos | Very simple to implement, supported everywhere | Sends credentials every request, poor fit for public clients, doesn't scale well |
| API keys/Opaque Tokens | Service-to-service APIs, scripts, partner integrations | Easy to rotate, safer than sharing passwords | Requires token lookup, limited identity information |
| JWT | SPAs, mobile apps, distributed APIs, microservices | Stateless, scalable, supports roles/claims | Harder revocation, signing key management |
| Session and Cookies | Traditional server-rendered web apps | Great browser user experience, easy logout/revocation | Session storage, CSRF concerns, scaling complexity |
| OAuth2 and OIDC | Enterprise apps, SSO, third-party login | Delegated access, centralised identity, SSO support | More concepts, setup and moving parts |
Opinion time: there isn’t a universally "best" security approach as each approach usually solves different problems. The use case is usually based on the purpose of the app and the scale of the app. Simpler/internal applications may only need Basic Auth or sessions, while larger, public facing systems often move toward JWT or OAuth2/OpenID Connect as requirements grow.
Thanks for reading and I hope this was helpful!