This guide covers security best practices in OpenDataChat.
Security is fundamental to protecting user data and maintaining system integrity.
// lib/security/sql.ts
import { Prisma } from '@prisma/client';
// NEVER: String concatenation
const bad = `SELECT * FROM users WHERE id = '${userId}'`;
// GOOD: Parameterized queries
const good = await prisma.$queryRaw`
SELECT * FROM users WHERE id = ${userId}
`;
// GOOD: Prisma ORM
const user = await prisma.user.findUnique({
where: { id: userId },
});// lib/security/xss.ts
import DOMPurify from 'isomorphic-dompurify';
export function sanitizeHTML(dirty: string): string {
return DOMPurify.sanitize(dirty, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'p'],
ALLOWED_ATTR: ['href', 'target'],
});
}
// React automatically escapes
function SafeComponent({ userInput }: { userInput: string }) {
return <div>{userInput}</div>; // Automatically escaped
}// lib/security/csrf.ts
import { randomBytes } from 'crypto';
export function generateCSRFToken(): string {
return randomBytes(32).toString('hex');
}
export async function validateCSRFToken(
sessionToken: string,
requestToken: string
): Promise<boolean> {
return sessionToken === requestToken;
}// lib/auth/password.ts
import bcrypt from 'bcryptjs';
const SALT_ROUNDS = 12;
export async function hashPassword(password: string): Promise<string> {
return bcrypt.hash(password, SALT_ROUNDS);
}
export async function verifyPassword(
password: string,
hash: string
): Promise<boolean> {
return bcrypt.compare(password, hash);
}// lib/auth/jwt.ts
import jwt from 'jsonwebtoken';
const JWT_SECRET = process.env.JWT_SECRET!;
const ACCESS_TOKEN_EXPIRY = '15m';
const REFRESH_TOKEN_EXPIRY = '7d';
export function generateTokens(userId: string) {
const accessToken = jwt.sign({ userId, type: 'access' }, JWT_SECRET, {
expiresIn: ACCESS_TOKEN_EXPIRY,
});
const refreshToken = jwt.sign({ userId, type: 'refresh' }, JWT_SECRET, {
expiresIn: REFRESH_TOKEN_EXPIRY,
});
return { accessToken, refreshToken };
}// lib/security/secrets.ts
// Use environment variables
const apiKey = process.env.API_KEY;
// Never log secrets
logger.info({ userId }, 'User action'); // OK
logger.info({ password }, 'Login'); // BAD
// Redact in logs
const logger = pino({
redact: ['password', 'token', 'apiKey', 'secret', 'authorization'],
});// middleware/rate-limit.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(100, '1 m'),
});
export async function rateLimitMiddleware(request: NextRequest) {
const ip = request.ip ?? '127.0.0.1';
const { success, limit, remaining } = await ratelimit.limit(ip);
if (!success) {
return new Response('Too Many Requests', { status: 429 });
}
return null;
}// next.config.js
const securityHeaders = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
{ key: 'X-Frame-Options', value: 'DENY' },
{ key: 'X-XSS-Protection', value: '1; mode=block' },
{ key: 'Referrer-Policy', value: 'strict-origin-when-cross-origin' },
{ key: 'Content-Security-Policy', value: "default-src 'self'" },
];- Validate all input on both client and server
- Use parameterized queries to prevent SQL injection
- Escape output to prevent XSS
- Implement proper authentication with secure tokens
- Use HTTPS everywhere in production
- Keep dependencies updated for security patches
- Log security events for monitoring and auditing