Production-ready React SPA security guidelines and best practices.
Version: 1.0.0
Last Updated: February 8, 2026
- Reporting Vulnerabilities
- Supported Versions
- Security Features
- Best Practices
- Common Vulnerabilities
- Dependency Security
- Compliance
We take security seriously. If you discover a security vulnerability, please report it responsibly.
Email: security@dcyfr.ai
Subject: [SECURITY] @dcyfr/ai-react - Brief description
Include:
- Vulnerability description
- Steps to reproduce
- Potential impact
- Suggested fix (if available)
Response Time: We aim to respond within 48 hours and provide a fix within 7 days for critical vulnerabilities.
- Acknowledgment within 48 hours
- Assessment and severity classification
- Fix development and testing
- Coordinated disclosure (you'll be credited unless you prefer anonymity)
- Patch release with security advisory
| Version | Supported | End of Life |
|---|---|---|
| 1.x.x | ✅ Yes | TBD |
| 0.x.x | ❌ No | Feb 2026 |
Recommendation: Always use the latest v1.x release for security patches and updates.
-
XSS Protection
- React's automatic escaping prevents injection attacks
- Avoid
dangerouslySetInnerHTMLunless absolutely necessary - Sanitize user content with libraries like DOMPurify
-
Type Safety
- TypeScript strict mode enabled
- Runtime validation with Zod schemas
- Type-safe API clients prevent data corruption
-
Secure Dependencies
- Vetted dependencies from trusted sources
- Regular security audits with
npm audit - Automated updates via Dependabot
-
Content Security Policy (CSP)
- Configure in
vite.config.tsfor production builds - Restrict script sources to prevent XSS
- Configure in
❌ AVOID: Storing tokens in localStorage (vulnerable to XSS)
// DON'T DO THIS
localStorage.setItem('token', authToken);✅ RECOMMENDED: Use HttpOnly cookies
// Set via server response with HttpOnly flag
// Client-side: No direct access to token (safer)Alternative (if cookies not feasible): Use sessionStorage with short TTL
// Acceptable for short-lived sessions
sessionStorage.setItem('token', authToken);import { apiClient } from '@/services';
import { z } from 'zod';
const loginSchema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
async function login(credentials: unknown) {
// 1. Validate input
const validated = loginSchema.parse(credentials);
// 2. Send to server (use HTTPS in production)
const response = await apiClient.post('/auth/login', validated);
// 3. Server sets HttpOnly cookie
// Client doesn't handle token directly
return response;
}Always validate user inputs with Zod schemas:
import { z } from 'zod';
const userInputSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().min(0).max(150),
});
function handleFormSubmit(data: unknown) {
try {
const validated = userInputSchema.parse(data);
// Safe to use validated data
} catch (error) {
// Handle validation errors
console.error('Invalid input:', error);
}
}Rule: Never trust client-side data. Validate on both client AND server.
Production requirement: All API calls must use HTTPS.
// vite.config.ts
export default defineConfig({
server: {
https: true, // Enable for local testing
},
});Include CSRF tokens for state-changing requests:
async function updateProfile(data: ProfileData) {
const csrfToken = getCsrfToken(); // From cookie or meta tag
await apiClient.put('/api/profile', data, {
headers: {
'X-CSRF-Token': csrfToken,
},
});
}Implement client-side rate limiting for sensitive operations:
import { useDebounce } from '@/hooks';
function SearchBar() {
const [query, setQuery] = useState('');
const debouncedQuery = useDebounce(query, 500); // Limit API calls
useEffect(() => {
if (debouncedQuery) {
apiClient.get(`/search?q=${debouncedQuery}`);
}
}, [debouncedQuery]);
}If rendering user-generated HTML (e.g., rich text), sanitize it:
import DOMPurify from 'dompurify';
function UserContent({ html }: { html: string }) {
const sanitized = DOMPurify.sanitize(html);
return <div dangerouslySetInnerHTML={{ __html: sanitized }} />;
}WARNING: Only use dangerouslySetInnerHTML with sanitized content.
Validate file types and sizes:
const fileUploadSchema = z.object({
file: z
.custom<File>()
.refine((file) => file.size <= 5 * 1024 * 1024, 'File must be ≤ 5MB')
.refine((file) => ['image/png', 'image/jpeg'].includes(file.type), 'Only PNG/JPEG allowed'),
});
function FileUpload() {
const handleUpload = (file: File) => {
try {
fileUploadSchema.parse({ file });
// Safe to upload
} catch (error) {
alert('Invalid file');
}
};
}Run security audits regularly:
# Check for vulnerabilities
npm audit
# Fix automatically (non-breaking)
npm audit fix
# Fix all (may include breaking changes)
npm audit fix --forceKeep dependencies up-to-date:
# Check for outdated packages
npm outdated
# Update to latest (within semver range)
npm update
# Update major versions (review breaking changes)
npx npm-check-updates -u
npm installEnable Dependabot in GitHub:
- Go to Settings > Security > Dependabot
- Enable Dependency alerts
- Enable Security updates
Risk: Injecting malicious scripts via user input.
Mitigation:
// ✅ SAFE: React escapes by default
function UserName({ name }: { name: string }) {
return <div>{name}</div>; // Automatically escaped
}
// ❌ DANGER: Direct HTML injection
function UnsafeName({ html }: { html: string }) {
return <div dangerouslySetInnerHTML={{ __html: html }} />; // Vulnerable!
}
// ✅ SAFE: Sanitize first
import DOMPurify from 'dompurify';
function SafeName({ html }: { html: string }) {
return <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(html) }} />;
}Risk: Unauthorized actions via forged requests.
Mitigation:
- Use SameSite cookies
- Validate CSRF tokens
- Require authentication for sensitive actions
// Server sets SameSite cookie
res.cookie('sessionId', token, {
httpOnly: true,
secure: true, // HTTPS only
sameSite: 'strict',
});
// Client includes CSRF token
await apiClient.post('/api/transfer', data, {
headers: {
'X-CSRF-Token': csrfToken,
},
});Risk: Embedding site in malicious iframe.
Mitigation: Add X-Frame-Options header (configured server-side).
// Prevent iframe embedding
res.setHeader('X-Frame-Options', 'DENY');
// Or allow same origin only
res.setHeader('X-Frame-Options', 'SAMEORIGIN');Risk: Redirecting users to malicious sites.
Mitigation: Validate redirect URLs.
function SafeRedirect({ to }: { to: string }) {
const navigate = useNavigate();
const handleRedirect = () => {
// Validate internal URLs only
if (to.startsWith('/') && !to.startsWith('//')) {
navigate({ to });
} else {
console.error('Invalid redirect URL');
}
};
return <button onClick={handleRedirect}>Continue</button>;
}Requirements:
- Obtain consent for cookies/tracking
- Provide data export/deletion mechanisms
- Include privacy policy link
Implementation:
function CookieConsent() {
const [accepted, setAccepted] = useLocalStorage('cookieConsent', false);
if (accepted) return null;
return (
<div className="fixed bottom-0 w-full bg-gray-800 text-white p-4">
<p>We use cookies to improve your experience.</p>
<button onClick={() => setAccepted(true)}>Accept</button>
</div>
);
}| Vulnerability | Status | Mitigation |
|---|---|---|
| A01: Broken Access Control | ✅ | Client-side route guards + server-side validation |
| A02: Cryptographic Failures | ✅ | HTTPS enforced, secure token storage |
| A03: Injection | ✅ | Zod validation, prepared statements (server) |
| A04: Insecure Design | ✅ | Security-first architecture |
| A05: Security Misconfiguration | ✅ | Secure defaults, CSP headers |
| A06: Vulnerable Components | ✅ | Automated dependency scanning |
| A07: Identity Failures | ✅ | HttpOnly cookies, MFA support ready |
| A08: Software/Data Integrity | ✅ | SRI for CDN resources |
| A09: Logging Failures | Implement server-side logging | |
| A10: SSRF | N/A | Not applicable (client-side only) |
Before deploying to production:
- Enable HTTPS for all API requests
- Configure Content Security Policy (CSP)
- Use HttpOnly cookies for authentication
- Validate all user inputs with Zod
- Run
npm auditand fix vulnerabilities - Enable rate limiting on API endpoints
- Implement CSRF protection
- Add X-Frame-Options header
- Sanitize user-generated content
- Review third-party integrations
- Enable Dependabot alerts
- Add privacy policy and cookie consent
- Configure secure session timeouts
- Test with OWASP ZAP or similar scanner
In case of a security breach:
- Isolate: Immediately revoke compromised credentials
- Assess: Determine scope and impact
- Notify: Alert affected users within 72 hours (GDPR requirement)
- Fix: Deploy patches and security updates
- Review: Conduct post-mortem and improve processes
- OWASP React Security Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/React_Security_Cheat_Sheet.html
- npm Security Best Practices: https://docs.npmjs.com/security-best-practices
- Vite Security: https://vitejs.dev/guide/security.html
- MDN Web Security: https://developer.mozilla.org/en-US/docs/Web/Security
Security Team: security@dcyfr.ai
Bug Reports: https://github.com/dcyfr/dcyfr-ai-react/security/advisories
General Support: hello@dcyfr.ai
Last Updated: February 8, 2026
License: MIT
Maintained By: DCYFR Security Team