Security Basics Every Web Developer Should Know
Security is often treated as a specialized topic, something handled by backend engineers, security teams, or infrastructure. But web security is not a separate discipline that lives “somewhere else.”
Security is a direct outcome of how web systems are designed. Even if you only write HTML, CSS, and JavaScript, you are still building interfaces that:
- Accept user input
- Send data across networks
- Depend on identity and permissions
- Run in hostile environments
The web is not a trusted space. Every browser runs on a user-controlled device. Every request can be inspected and modified. Every input can be malicious.
The most important rule: the client is not trustworthy
One of the most important security concepts in web development is also one of the simplest, “Never trust the client.” The browser is not a secure environment. Users can disable JavaScript, edit HTML, modify requests, bypass UI restrictions, call APIs directly, and automate actions at scale.
This is not a rare behavior. It is normal. Tools like browser DevTools, proxies, and scripts make it easy. This means that any rule that matters must be enforced on the server. The UI can guide users, improve usability, and reduce mistakes, but it cannot enforce truth.
Security problems usually happen at boundaries:
- Between user input and system logic
- Between one user and another user’s data
- Between trusted and untrusted content
- Between the browser and the server
Most vulnerabilities are not caused by “hackers being clever.” They are caused by systems assuming trust where trust does not exist.
Authentication vs. authorization
These two concepts are often confused. They are often used together, but they describe two different responsibilities.
Authentication answers, “Who is this user?” It is the process of proving identity. Logging in with a password, signing in with Google, or presenting a valid session cookie are all forms of authentication. Once authentication succeeds, the system knows which user is making the request.
Authorization answers, “What is this user allowed to do?” Authorization is not about identity. It is about permissions. It determines whether a user can access a resource, perform an action, or view certain data. Authorization rules are what separate normal users from admins, free users from paid users, and one user’s account from another user’s account.
This distinction matters because a user can be fully authenticated and still unauthorized for a specific action. For example:
- A logged-in user is not allowed to access another user’s account.
- A basic plan user is not allowed to access pro features.
- A normal user is not allowed to perform admin actions.
A dangerous mistake is treating authorization as a UI concern. Many applications hide buttons, disable links, or conditionally render features based on the user’s role. This improves usability, but it does not provide security. Hiding a button is not a permission system. It only changes what the user sees, not what the user can do.
On the web, users can bypass the interface entirely. They can call APIs directly, replay requests, modify parameters, and send actions that the UI never exposed. If the server does not enforce authorization rules, the system is vulnerable even if the interface looks correct.
This is why authorization must be enforced at the server boundary. Every request that changes data, reads private information, or accesses restricted functionality must be checked against permissions on the server.
The browser is designed to protect users, but only if you cooperate
The modern web has strong security features built in. Modern browsers actively enforce security boundaries to protect users from malicious behavior through mechanisms like:
- The same-origin policy
- Secure cookies
- Sandboxing
- Permission prompts
- Mixed-content blocking
But these protections are not automatic. Developers can weaken them accidentally by embedding untrusted content unsafely, exposing sensitive data in URLs, storing tokens insecurely, or enabling overly permissive cross-origin requests. In these cases, the browser is still enforcing its rules, but the system has been designed in a way that bypasses their benefits.
A useful way to think about browser security is this: The browser provides strong boundaries, but the application decides whether those boundaries are respected.
When systems are designed with those boundaries in mind, many classes of attacks become much harder. When they are ignored or misunderstood, vulnerabilities appear even in otherwise well-written code.
Data handling
Security is not only about attacks. It is also about responsibility. If a system collects user data, it must handle it carefully. This includes:
- Transmitting data securely (HTTPS)
- Storing sensitive data appropriately
- Limiting access based on permissions
- Minimizing what is collected in the first place
Many real-world security failures are not dramatic “hacks.” They are the result of careless data practices. Logging secrets for debugging, exposing private information in client-side code, or keeping unnecessary data indefinitely all increase the blast radius of failure. When something goes wrong, the impact is larger than it needed to be.
Good security starts with restraint. For example:
- If data is not collected, it cannot be leaked.
- If access is limited, damage is contained.
- If sensitive information is handled deliberately, mistakes are less costly.
Input validation is a security feature, not just a usability feature
Validation is often presented primarily as a user experience concern. It helps users understand expectations, catches obvious errors early, and simplifies form completion. However, validation also fulfills a critical security role.
Every input transmitted to a server must be considered untrusted. Even if data originates from a clean, well-designed form, it can be intercepted and altered before submission. Users can disable client-side JavaScript, alter HTTP requests, or even submit data that never interacted with your official interface. Attackers rarely use your predefined UI; instead, they interact directly with your system’s underlying APIs and endpoints. This fundamental principle necessitates validation on both sides of the application boundary: client and server.
Client-side validation significantly improves usability. It guides users through the input process and reduces frustration. However, it cannot be solely trusted to protect the integrity of the system. Server-side validation, conversely, is crucial for enforcing business rules, protecting data integrity, and ensuring overall system correctness. Any critical rule must be validated on the server, irrespective of what occurs in the browser.
In summary, both client-side and server-side validation are indispensable, and neither can effectively replace the other.
Common misconceptions that lead to insecure systems
Many insecure systems are not the result of ignorance. They are the result of common misconceptions:
- “If the UI prevents it, users can’t do it.”
- “If I validate input in JavaScript, the server is safe.”
- “If the API is not documented, it won’t be found.”
These assumptions fail not because developers are careless, but because the web is fundamentally transparent. The browser exposes requests. The network reveals behavior. The client is under the user’s control. Any system that relies on secrecy or UI constraints for security is fragile by design.
Secure systems are built by rejecting these assumptions early. On the web:
- The UI is guidance, not enforcement
- The client is untrusted by default
- Every endpoint must defend itself
- Every rule that matters must live on the server
Once these ideas are internalized, security becomes a matter of clear boundaries and consistent enforcement rather than defensive coding.
Popular Guides