Skip to content

Security: chenwenxiaolive/OpenDataChat

Security

docs/security.md

Security Guide

This guide covers security best practices in OpenDataChat.

Security Overview

Security is fundamental to protecting user data and maintaining system integrity.

Input Validation

SQL Injection Prevention

// 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 },
});

XSS Prevention

// 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
}

CSRF Protection

// 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;
}

Authentication Security

Password Hashing

// 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);
}

JWT Security

// 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 };
}

Secrets Management

// 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'],
});

Rate Limiting

// 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;
}

Security Headers

// 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'" },
];

Best Practices

  1. Validate all input on both client and server
  2. Use parameterized queries to prevent SQL injection
  3. Escape output to prevent XSS
  4. Implement proper authentication with secure tokens
  5. Use HTTPS everywhere in production
  6. Keep dependencies updated for security patches
  7. Log security events for monitoring and auditing

There aren’t any published security advisories