Why this matters (fast TL;DR)
Sensitive data exposure in React often happens in the browser bundle—API keys, tokens, stack traces, or logs shipped to users. Below are concise patterns to avoid leaks and ship safely.
Common Leak Paths in React
- Secrets baked into the bundle (
.env
misuse) - Tokens in
localStorage
or query strings - Verbose error overlays & stack traces in production
- Console logs/analytics capturing PII
- Unprotected API endpoints called directly from the UI
Coding Examples (Do → Don’t)
1) Don’t ship secrets to the client
// ❌ BAD: key in the bundle
export const stripeKey = "sk_live_ABC123";
// ✅ GOOD: call your server; keep secret there
export async function pay(amount) {
const res = await fetch("/api/pay", { method: "POST", body: JSON.stringify({ amount }) });
return res.json();
}
Screenshot of our Website Vulnerability Scanner tool homepage
Screenshot of the free tools webpage where you can access security assessment tools.
2) .env
in React ≠ secret storage
Anything exposed to the browser is public.
# CRA/Vite/Next: Only PUBLIC_* (or NEXT_PUBLIC_*) is readable client-side.
VITE_PUBLIC_API_BASE=https://api.example.com
// Use the public base URL only; server keeps real secrets.
fetch(`${import.meta.env.VITE_PUBLIC_API_BASE}/products`);
3) Use a tiny proxy on the server
// server.js (Express) — secrets live here, not in React.
import express from "express";
import fetch from "node-fetch";
const app = express();
app.post("/api/pay", async (req, res) => {
const r = await fetch("https://payments.example.com/charge", {
method: "POST",
headers: { Authorization: `Bearer ${process.env.PAYMENT_KEY}` },
body: req
});
res.status(r.status).send(await r.text());
});
app.listen(3000);
4) Prefer HttpOnly cookies over localStorage
// ❌ BAD: XSS can read tokens
localStorage.setItem("token", jwt);
// ✅ GOOD: server sets HttpOnly, Secure, SameSite cookies
// (client reads auth state via a lightweight /me endpoint)
5) Strip logs & guard errors
// Remove console.* in production (Babel/TS transformer or build plugin)
if (process.env.NODE_ENV === "production") {
console.log = () => {};
console.error = () => {};
}
// Error boundary to avoid leaking stack details to users
function SafeBoundary({ children }) {
return (
<ErrorBoundary fallback={<p>Something went wrong.</p>}>
{children}
</ErrorBoundary>
);
}
Quick Security Checklist for React
- [ ] No secrets in source, env, or bundle
- [ ] Tokens in HttpOnly cookies; never in URLs
- [ ] Production builds hide stack traces & strip logs
- [ ] All sensitive calls go through a server proxy
- [ ] CSP, HTTPS, Secure/SameSite cookies enabled
** Sample Vulnerability Report** by our free scanner to check Website Vulnerability
Sample vulnerability assessment report generated with our free tool, providing insights into possible vulnerabilities.
Scan your site now (Free)
Run your React site through our Free Website Vulnerability Scanner to spot exposures fast: https://free.pentesttesting.com/
Explore more guides on our blog: https://www.pentesttesting.com/blog/
Related Services (we can help)
Managed IT Services: end-to-end ops with security baked in
https://www.pentesttesting.com/managed-it-services/AI Application Cybersecurity: secure LLM & ML-powered apps
https://www.pentesttesting.com/ai-application-cybersecurity/Offer Cybersecurity to Your Clients: white-label partner program
https://www.pentesttesting.com/offer-cybersecurity-service-to-your-client/
Subscribe on LinkedIn
https://www.linkedin.com/build-relation/newsletter-follow?entityUrn=7327563980778995713
Pro tip: After fixes, rebuild your app and re-scan with the free tool to confirm no secrets or verbose traces are shipped.
Top comments (0)