Security Overview

WI-CARPOOL implements comprehensive security measures to protect user data and ensure safe ride-sharing experiences.

🔐 Authentication

Multi-factor authentication with JWT tokens and session management.

🛡️ Data Protection

End-to-end encryption for sensitive data and secure API communications.

🔍 Input Validation

Comprehensive input sanitization and validation on both client and server.

📊 Monitoring

Real-time security monitoring and incident response systems.

Authentication Security

Secure Token Management

// Token storage and management
class TokenManager {
  private static readonly ACCESS_TOKEN_KEY = 'access_token'
  private static readonly REFRESH_TOKEN_KEY = 'refresh_token'

  static setTokens(accessToken: string, refreshToken: string) {
    // Store in httpOnly cookies (preferred) or secure localStorage
    if (this.isSecureContext()) {
      document.cookie = `${this.ACCESS_TOKEN_KEY}=${accessToken}; Secure; SameSite=Strict; HttpOnly`
      document.cookie = `${this.REFRESH_TOKEN_KEY}=${refreshToken}; Secure; SameSite=Strict; HttpOnly`
    } else {
      // Fallback to localStorage with encryption
      const encryptedAccess = this.encrypt(accessToken)
      const encryptedRefresh = this.encrypt(refreshToken)
      localStorage.setItem(this.ACCESS_TOKEN_KEY, encryptedAccess)
      localStorage.setItem(this.REFRESH_TOKEN_KEY, encryptedRefresh)
    }
  }

  static getAccessToken(): string | null {
    if (this.isSecureContext()) {
      return this.getCookie(this.ACCESS_TOKEN_KEY)
    }
    const encrypted = localStorage.getItem(this.ACCESS_TOKEN_KEY)
    return encrypted ? this.decrypt(encrypted) : null
  }

  static clearTokens() {
    if (this.isSecureContext()) {
      document.cookie = `${this.ACCESS_TOKEN_KEY}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
      document.cookie = `${this.REFRESH_TOKEN_KEY}=; expires=Thu, 01 Jan 1970 00:00:00 GMT`
    } else {
      localStorage.removeItem(this.ACCESS_TOKEN_KEY)
      localStorage.removeItem(this.REFRESH_TOKEN_KEY)
    }
  }

  private static isSecureContext(): boolean {
    return window.isSecureContext && location.protocol === 'https:'
  }

  private static encrypt(data: string): string {
    // Implement client-side encryption
    return btoa(data) // Simple base64 for demo, use proper encryption
  }

  private static decrypt(data: string): string {
    // Implement client-side decryption
    return atob(data) // Simple base64 for demo, use proper decryption
  }
}

API Request Security

// Secure API client with automatic token refresh
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios'

class SecureApiClient {
  private client: AxiosInstance
  private isRefreshing = false
  private failedQueue: Array<{ resolve: Function; reject: Function }> = []

  constructor() {
    this.client = axios.create({
      baseURL: process.env.VITE_API_BASE_URL,
      timeout: 30000,
      withCredentials: true,
    })

    this.setupInterceptors()
  }

  private setupInterceptors() {
    // Request interceptor - add auth header
    this.client.interceptors.request.use(
      (config) => {
        const token = TokenManager.getAccessToken()
        if (token) {
          config.headers.Authorization = `Bearer ${token}`
        }
        
        // Add CSRF token if available
        const csrfToken = this.getCSRFToken()
        if (csrfToken) {
          config.headers['X-CSRF-Token'] = csrfToken
        }

        return config
      },
      (error) => Promise.reject(error)
    )

    // Response interceptor - handle token refresh
    this.client.interceptors.response.use(
      (response) => response,
      async (error) => {
        const originalRequest = error.config

        if (error.response?.status === 401 && !originalRequest._retry) {
          if (this.isRefreshing) {
            return new Promise((resolve, reject) => {
              this.failedQueue.push({ resolve, reject })
            }).then((token) => {
              originalRequest.headers.Authorization = `Bearer ${token}`
              return this.client(originalRequest)
            })
          }

          originalRequest._retry = true
          this.isRefreshing = true

          try {
            const newToken = await this.refreshToken()
            this.processQueue(null, newToken)
            originalRequest.headers.Authorization = `Bearer ${newToken}`
            return this.client(originalRequest)
          } catch (refreshError) {
            this.processQueue(refreshError, null)
            TokenManager.clearTokens()
            window.location.href = '/signin'
            return Promise.reject(refreshError)
          } finally {
            this.isRefreshing = false
          }
        }

        return Promise.reject(error)
      }
    )
  }

  private getCSRFToken(): string | null {
    const meta = document.querySelector('meta[name="csrf-token"]')
    return meta?.getAttribute('content') || null
  }

  private async refreshToken(): Promise<string> {
    const refreshToken = TokenManager.getRefreshToken()
    if (!refreshToken) {
      throw new Error('No refresh token available')
    }

    const response = await axios.post('/api/auth/refresh', {
      refresh_token: refreshToken
    })

    const { access_token, refresh_token: newRefreshToken } = response.data
    TokenManager.setTokens(access_token, newRefreshToken)
    return access_token
  }

  private processQueue(error: any, token: string | null) {
    this.failedQueue.forEach(({ resolve, reject }) => {
      if (error) {
        reject(error)
      } else {
        resolve(token)
      }
    })
    
    this.failedQueue = []
  }
}

Input Validation & Sanitization

Form Input Sanitization

// Input sanitization utilities
import DOMPurify from 'dompurify'

export class InputSanitizer {
  static sanitizeHtml(html: string): string {
    return DOMPurify.sanitize(html, {
      ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
      ALLOWED_ATTR: ['href'],
      ALLOW_DATA_ATTR: false,
    })
  }

  static sanitizeText(text: string): string {
    return text
      .replace(/[<>"']/g, (char) => {
        const map: Record<string, string> = {
          '&': '&',
          '<': '<',
          '>': '>',
          '"': '"',
          "'": '''
        }
        return map[char]
      })
      .trim()
  }

  static validateEmail(email: string): boolean {
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
    return emailRegex.test(email) && email.length <= 254
  }

  static validatePhoneNumber(phone: string): boolean {
    const phoneRegex = /^\+[1-9]\d{1,14}$/
    return phoneRegex.test(phone)
  }

  static sanitizeFileName(fileName: string): string {
    return fileName
      .replace(/[^a-zA-Z0-9.-]/g, '_')
      .substring(0, 255)
  }

  static validateUrl(url: string): boolean {
    try {
      const parsed = new URL(url)
      return ['http:', 'https:'].includes(parsed.protocol)
    } catch {
      return false
    }
  }
}

// Usage in forms
function useSecureForm<T>(schema: z.ZodSchema<T>) {
  const form = useForm<T>({
    resolver: zodResolver(schema),
  })

  const secureSubmit = form.handleSubmit((data) => {
    // Sanitize all string fields
    const sanitizedData = Object.entries(data).reduce((acc, [key, value]) => {
      if (typeof value === 'string') {
        acc[key] = InputSanitizer.sanitizeText(value)
      } else {
        acc[key] = value
      }
      return acc
    }, {} as any)

    return onSubmit(sanitizedData)
  })

  return { ...form, handleSubmit: secureSubmit }
}

Content Security Policy

CSP Configuration

<!-- Content Security Policy meta tag -->
<meta http-equiv="Content-Security-Policy" content="
  default-src 'self';
  script-src 'self' 'unsafe-inline' https://maps.googleapis.com https://js.stripe.com;
  style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;
  img-src 'self' data: https: blob:;
  connect-src 'self' https://api.wicarpool.com wss://api.wicarpool.com;
  frame-src 'self' https://js.stripe.com;
  object-src 'none';
  base-uri 'self';
  form-action 'self';
  upgrade-insecure-requests;
">

File Upload Security

Secure File Upload

// Secure file upload component
import { useState } from 'react'

interface SecureFileUploaderProps {
  onUpload: (file: File) => void
  accept: string[]
  maxSize: number
}

export function SecureFileUploader({ onUpload, accept, maxSize }: SecureFileUploaderProps) {
  const [error, setError] = useState<string | null>(null)

  const validateFile = (file: File): boolean => {
    setError(null)

    // Check file size
    if (file.size > maxSize) {
      setError(`File size must be less than ${maxSize / (1024 * 1024)}MB`)
      return false
    }

    // Check file type
    if (!accept.includes(file.type)) {
      setError(`File type ${file.type} is not allowed`)
      return false
    }

    // Check file extension
    const extension = file.name.split('.').pop()?.toLowerCase()
    const allowedExtensions = accept.map(type => type.split('/')[1])
    if (!extension || !allowedExtensions.includes(extension)) {
      setError('File extension does not match MIME type')
      return false
    }

    // Validate file header (magic numbers)
    return validateFileHeader(file)
  }

  const validateFileHeader = (file: File): Promise<boolean> => {
    return new Promise((resolve) => {
      const reader = new FileReader()
      reader.onload = (e) => {
        const arrayBuffer = e.target?.result as ArrayBuffer
        const uint8Array = new Uint8Array(arrayBuffer.slice(0, 8))
        
        // Check magic numbers for common image types
        const header = Array.from(uint8Array)
          .map(b => b.toString(16).padStart(2, '0'))
          .join('')

        const validHeaders = {
          'image/jpeg': ['ffd8ffe0', 'ffd8ffe1', 'ffd8ffe2'],
          'image/png': ['89504e47'],
          'image/webp': ['52494646'],
        }

        const isValid = validHeaders[file.type as keyof typeof validHeaders]
          ?.some(validHeader => header.startsWith(validHeader)) ?? false

        if (!isValid) {
          setError('File header validation failed')
        }

        resolve(isValid)
      }
      reader.readAsArrayBuffer(file.slice(0, 8))
    })
  }

  const handleFileSelect = async (event: React.ChangeEvent<HTMLInputElement>) => {
    const file = event.target.files?.[0]
    if (!file) return

    if (await validateFile(file)) {
      onUpload(file)
    }
  }

  return (
    <div>
      <input
        type="file"
        onChange={handleFileSelect}
        accept={accept.join(',')}
      />
      {error && <p className="text-red-500 text-sm mt-1">{error}</p>}
    </div>
  )
}

Security Headers

Security Headers Configuration

// Security headers for Nginx/Apache
/*
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(self), microphone=(), camera=()
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
*/

// Runtime security checks
export function initSecurityChecks() {
  // Check if running on HTTPS in production
  if (process.env.NODE_ENV === 'production' && location.protocol !== 'https:') {
    console.error('Application must be served over HTTPS in production')
    // Redirect to HTTPS
    location.href = location.href.replace('http:', 'https:')
  }

  // Check for mixed content
  if (location.protocol === 'https:') {
    const insecureElements = document.querySelectorAll('[src^="http:"], [href^="http:"]')
    if (insecureElements.length > 0) {
      console.warn('Mixed content detected:', insecureElements)
    }
  }

  // Disable right-click in production (optional)
  if (process.env.NODE_ENV === 'production') {
    document.addEventListener('contextmenu', (e) => {
      e.preventDefault()
    })

    // Disable common developer shortcuts
    document.addEventListener('keydown', (e) => {
      if (
        (e.ctrlKey && e.shiftKey && e.key === 'I') || // DevTools
        (e.ctrlKey && e.shiftKey && e.key === 'J') || // Console
        (e.ctrlKey && e.key === 'U') || // View Source
        (e.key === 'F12') // DevTools
      ) {
        e.preventDefault()
      }
    })
  }
}

Security Best Practices

Authentication

  • Implement multi-factor authentication for sensitive operations
  • Use secure password requirements and hashing
  • Implement session timeout and idle detection
  • Log and monitor authentication attempts

Data Protection

  • Encrypt sensitive data at rest and in transit
  • Implement proper access controls and permissions
  • Sanitize and validate all user inputs
  • Use HTTPS for all communications

Frontend Security

  • Implement Content Security Policy (CSP)
  • Validate and sanitize data before rendering
  • Use secure storage methods for sensitive data
  • Regularly update dependencies and scan for vulnerabilities

Monitoring & Response

  • Implement security event logging and monitoring
  • Set up alerts for suspicious activities
  • Have an incident response plan in place
  • Conduct regular security audits and penetration testing