DEV Community

Cover image for 🔐 Frontend Security Best Practices Every Developer Should Know
Anisubhra Sarkar (Ani)
Anisubhra Sarkar (Ani)

Posted on • Edited on

🔐 Frontend Security Best Practices Every Developer Should Know

Security is no longer just a backend concern. In today’s web ecosystem, JavaScript frontend code is a frequent target for attacks—from XSS to leaked API keys. Whether you're building SPAs, PWAs, or dashboard-heavy apps, following frontend security best practices is critical.

Modern frontend apps aren’t just “views” anymore — they handle authentication, API calls, sensitive user data, and business logic. That makes the browser a prime target for attackers.

In this guide, we’ll break down the most common frontend security pitfalls, explain how attackers exploit them, and show how to secure your applications step by step.


1. Cross-Site Scripting (XSS)

Scenario: A user pastes <script>alert('XSS')</script> in a text field, and it executes.
Why it happens: The app renders raw user input directly in the DOM. Attackers can inject scripts to steal cookies, tokens, or perform actions on behalf of the user.

Fix:

  • Never use innerHTML with untrusted input.
  • Escape and sanitize inputs using libraries like DOMPurify.
  • Use frameworks like React/Angular/Vue, which escape values by default.
// ❌ Vulnerable
commentBox.innerHTML = userComment;

// ✅ Safe
commentBox.textContent = userComment;
Enter fullscreen mode Exit fullscreen mode

2. JWT Tokens in localStorage

Problem: Many apps store JWT tokens in localStorage for persistence. But if an attacker injects JS (via XSS), they can read the token and impersonate users.

Fix:

  • Store tokens in httpOnly, Secure cookies, which cannot be read by JavaScript.
  • Add short expiration times and refresh tokens securely.

3. Cross-Site Request Forgery (CSRF)

Scenario: A malicious site submits a form to your domain while a user is logged in.
Why it’s dangerous: Because cookies are automatically sent, attackers can trick users into performing actions (like transferring money).

Fix:

  • Use SameSite=Strict or Lax cookies.
  • Implement CSRF tokens for state-changing requests.

4. Cached Pages After Logout

Scenario: After logging out, hitting the browser’s back button still shows sensitive content.
Why it happens: The browser is serving cached pages, not checking authentication.

Fix:
Send cache-control headers for authenticated routes:

Cache-Control: no-store, no-cache, must-revalidate
Pragma: no-cache
Enter fullscreen mode Exit fullscreen mode

5. Secure Image Uploads

Problem: Image uploads are often abused to upload malicious files, oversized payloads, or disguised executables.

Fix:

  • Check file extension & MIME type (image/png, image/jpeg).
  • Limit file size (to prevent DoS via large uploads).
  • Re-process uploads server-side (never trust client validation alone).

6. Unsafe innerHTML

Why it’s risky: Dynamically injecting scripts, ads, or HTML with innerHTML opens you to XSS.

Fix:

  • Use textContent for raw text.
  • For templating, rely on frameworks or safe APIs like insertAdjacentHTML with sanitized inputs.

7. Iframe Embedding (Clickjacking)

Scenario: Your app is loaded inside an invisible iframe on a malicious site. Users think they’re clicking your UI, but they’re actually clicking attacker-controlled overlays.

Fix:
Block iframe embedding with security headers:

X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none';
Enter fullscreen mode Exit fullscreen mode

8. CDN Script Integrity

Problem: You include scripts from a CDN (e.g., jQuery). If the CDN is compromised, malicious code runs in your app.

Fix: Use Subresource Integrity (SRI) to ensure the script hasn’t been altered.

<script src="https://cdn.com/lib.js"
  integrity="sha384-abc123"
  crossorigin="anonymous"></script>
Enter fullscreen mode Exit fullscreen mode

9. Auto-Triggered Actions

Scenario: A button action is triggered automatically when opened from a link.
Why it’s bad: Violates user intent and can be abused for attacks like CSRF or spam.

Fix: Require explicit user interaction (onclick) before running sensitive actions.


10. Silent POST Requests (CSRF Redux)

Problem: Attackers silently trigger POST requests without user intent (hidden forms, image requests).

Fix:

  • Always verify CSRF tokens.
  • Add backend validation for actions (e.g., confirm password before deleting account).

11. Sensitive Data in URLs

Problem: Tokens, passwords, or API keys in query strings end up in logs, history, referrer headers.

Fix:

  • Pass secrets in request headers or body, never in URLs.
  • Example: Authorization: Bearer <token> instead of ?token=abc.

12. Exposing Environment Variables

Problem: In React/Next.js, variables prefixed with REACT_APP_ or NEXT_PUBLIC_ get shipped to the frontend. Storing secrets there leaks them.

Fix:

  • Only expose public keys (like Firebase project IDs).
  • Keep private keys server-side.

13. Insecure postMessage

Problem: window.postMessage lets sites talk across iframes. Without validating the sender, attackers can send malicious commands.

Fix: Always check the origin:

window.addEventListener("message", (e) => {
  if (e.origin !== "https://yourdomain.com") return;
  // safe to handle message
});
Enter fullscreen mode Exit fullscreen mode

14. Loose CORS Policies

Problem: Access-Control-Allow-Origin: * allows anyone to call your APIs.

Fix: Restrict origins:

Access-Control-Allow-Origin: https://yourapp.com
Enter fullscreen mode Exit fullscreen mode

15. Third-Party Scripts & Overlays

Risks:

  • Third-party scripts can log keystrokes, steal tokens.
  • Malicious overlays can hijack clicks.

Fix:

  • Only load trusted scripts.
  • Use sandboxed iframes for risky content.
  • Test UI regularly for hidden overlays.

16. Unvalidated Redirects

Problem: Redirect URLs taken from query params allow phishing attacks.

Fix: Maintain a whitelist of allowed redirect URLs.


17. Rate Limiting & Error Handling

Problems:

  • No rate limiting → brute force, spam.
  • Detailed errors → leak backend stack traces.

Fix:

  • Add frontend throttling + backend rate limiting.
  • Show generic error messages to users.

✅ Final Takeaways

  • Sanitize all user input to prevent XSS.
  • Store tokens in httpOnly cookies, never localStorage.
  • Use CSRF protections (SameSite cookies + tokens).
  • Harden your app with security headers (CSP, X-Frame-Options).
  • Treat third-party scripts and user uploads with caution.
  • Always validate on backend too — frontend checks alone are never enough.

🔒 Final Thoughts

As frontend applications grow in complexity, the attack surface expands. A single overlooked vulnerability — whether it’s an exposed token, an unsafe innerHTML, or a loose CORS policy — can compromise the entire system.

The key takeaway: security is not a feature you bolt on at the end. It’s a mindset and ongoing practice. Every design decision, code commit, and dependency you add should be evaluated with security in mind.

By making these best practices part of your daily development workflow, you’ll not only protect your users’ data but also build applications that earn their trust.

“Frontend security isn’t a one-time task — it’s an ongoing discipline. The best defense is to think like an attacker and patch every possible entry point.”


👉 If you've found this useful, follow me and give it a ❤️ for more deep dives into frontend security, performance, and modern JavaScript frameworks to stay ahead of common pitfalls and best practices.

Top comments (0)