All articles
SECURITY6 May 2026Rue Johnson

Common Security Practices in Applications

JWT vs sessions, cookies vs local storage, CORS, CSRF, XSS: these are not academic concepts. They are the difference between a secure application and one that leaks user data the first time someone pokes at it.

SecurityApplication SecurityAuthenticationOWASP
Common Security Practices in Applications

Common Security Practices in Applications

The Fundamentals That Stop Real Attacks

Authentication: JWT vs Sessions and When to Use Each:

JWTs (JSON Web Tokens) and server-side sessions solve the same problem differently: proving that a request comes from an authenticated user. JWTs are stateless; the server signs a token containing user claims and the client sends it with every request. The server verifies the signature without touching a database. This scales well for distributed systems and microservices where sharing session state across servers is painful. But JWTs come with tradeoffs: you cannot revoke a JWT before it expires without maintaining a blacklist, which defeats the stateless advantage. If a token is stolen, the attacker has access until expiration. Keep JWT lifetimes short (15 minutes) and pair them with refresh tokens stored in httpOnly cookies.

Server-side sessions store state on the backend (in Redis, a database, or memory) and send the client a session ID in a cookie. They are simpler to revoke: delete the session record and the user is logged out instantly. For most traditional web applications, especially those with a single backend, sessions are the more secure and practical choice. The session cookie should be httpOnly (inaccessible to JavaScript), Secure (HTTPS only), SameSite=Strict or Lax (CSRF protection), and scoped to the minimum necessary domain. Never store sensitive data in the JWT payload or session cookie itself; store a reference ID and look up the data server-side.

Storage Matters: Where You Put Tokens Changes Everything:

Local storage is accessible to any JavaScript running on your page. If your application has a single XSS vulnerability, an attacker's script can read every token in local storage and exfiltrate it to their server. This is not theoretical; it is one of the most common attack chains in web applications. Do not store authentication tokens, refresh tokens, or any sensitive data in local storage or session storage. Use httpOnly cookies instead. JavaScript cannot read httpOnly cookies, so even if an attacker achieves XSS, they cannot steal the token directly. They can still make authenticated requests from the user's browser (CSRF), but that is a separate problem with its own mitigations.

The one valid use case for storing tokens in memory (not local storage, actual JavaScript variables) is short-lived access tokens in single-page applications where the refresh token lives in an httpOnly cookie. On page reload, the SPA hits a refresh endpoint, gets a new access token, and holds it in memory for the session. This balances security with the UX requirements of SPAs. If your application stores JWTs in local storage right now, move them to httpOnly cookies. It is one of the highest-impact security improvements you can make with relatively low effort.

CORS, CSRF, and XSS: The Trinity You Cannot Ignore:

CORS (Cross-Origin Resource Sharing) controls which domains can make requests to your API. Set your Access-Control-Allow-Origin to your specific frontend domain, never use the wildcard (*) for authenticated endpoints. A misconfigured CORS policy lets any website make authenticated requests to your API using your users' cookies. CSRF (Cross-Site Request Forgery) tricks a user's browser into making unwanted requests to your application while they are authenticated. Mitigate it with SameSite cookies, anti-CSRF tokens, and verifying the Origin header on state-changing requests. If your API accepts a POST that transfers money and the only authentication is a cookie, you need CSRF protection.

XSS (Cross-Site Scripting) lets attackers inject malicious scripts into your application that execute in other users' browsers. It is consistently in the OWASP Top 10 because developers keep making the same mistakes: rendering user input without sanitization, using innerHTML instead of textContent, and trusting data from APIs without escaping it. Prevent XSS with output encoding (escape HTML entities before rendering), Content Security Policy headers (restrict which scripts can execute), and input validation (reject or sanitize input that does not match expected patterns). Never trust client-side input for anything; validate and sanitize on the server.

How MajorLinkx Secures Client Applications:

Every application we build follows a security baseline that is non-negotiable. Authentication uses httpOnly cookies with short-lived tokens. CORS is locked to known origins. All user input is validated on the server with strict schemas (we use Zod or class-validator depending on the stack). CSP headers are configured to block inline scripts and restrict resource origins. We run Semgrep in CI to catch injection patterns, and we test authentication flows manually during code review, not just through automated tests. SQL queries use parameterized statements exclusively; ORMs handle this, but we verify it in raw query escape hatches.

We also handle the practices that get overlooked: rate limiting on login endpoints to prevent credential stuffing, account lockout policies with exponential backoff, secure password hashing with bcrypt or Argon2 (never MD5, never SHA-256 without a salt), and proper error messages that do not leak whether an email exists in the system. These details are boring, repetitive, and absolutely critical. The applications that get breached are not the ones with exotic attack surfaces; they are the ones that skipped the basics. If you are building a web application and any of the terms in this article are unfamiliar, bring in someone who knows them before you ship.