Files
Portfolio/blog-posts/90-saas-security-compliance.md
T
damjan_savicandClaude Opus 4.5 43484c5023 Add blog posts, cleanup unused files, update components
- Add 100 blog posts covering AI, development, and tech topics
- Add .env.example for environment configuration
- Add accessibility and lighthouse audit scripts
- Remove obsolete SEO reports and temporary files
- Remove dev-dist build artifacts and backup files
- Remove unused portrait images (moved/consolidated elsewhere)
- Update contact form and component improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:42:11 +01:00

20 KiB
Raw Blame History

SaaS Security & Compliance

Meta-Description: SaaS Security Best Practices. SOC 2, GDPR Compliance, Data Encryption und Security Architecture für B2B SaaS.

Keywords: SaaS Security, SOC 2, GDPR, Data Encryption, Authentication, Compliance, Security Architecture


Einführung

Security ist für B2B SaaS nicht optional es ist Verkaufsargument. Von SOC 2 bis GDPR erwarten Enterprise-Kunden nachweisbare Sicherheit. Dieser Guide zeigt Security Best Practices und Compliance-Anforderungen.


Security Architecture Overview

┌─────────────────────────────────────────────────────────────┐
│              SAAS SECURITY ARCHITECTURE                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Defense in Depth:                                          │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  Layer 1: Network Security                          │   │
│  │  ├── WAF (Web Application Firewall)                │   │
│  │  ├── DDoS Protection                               │   │
│  │  ├── Rate Limiting                                 │   │
│  │  └── IP Whitelisting (optional)                    │   │
│  ├─────────────────────────────────────────────────────│   │
│  │  Layer 2: Application Security                      │   │
│  │  ├── Authentication (MFA, SSO)                     │   │
│  │  ├── Authorization (RBAC, ABAC)                    │   │
│  │  ├── Input Validation                              │   │
│  │  └── CSRF/XSS Protection                           │   │
│  ├─────────────────────────────────────────────────────│   │
│  │  Layer 3: Data Security                             │   │
│  │  ├── Encryption at Rest (AES-256)                  │   │
│  │  ├── Encryption in Transit (TLS 1.3)               │   │
│  │  ├── Key Management (KMS)                          │   │
│  │  └── Data Masking/Tokenization                     │   │
│  ├─────────────────────────────────────────────────────│   │
│  │  Layer 4: Infrastructure Security                   │   │
│  │  ├── Private Networks (VPC)                        │   │
│  │  ├── Security Groups                               │   │
│  │  ├── Secrets Management                            │   │
│  │  └── Container Security                            │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
│  Compliance Frameworks:                                     │
│  ├── SOC 2 Type II                                         │
│  ├── GDPR                                                  │
│  ├── HIPAA (Healthcare)                                    │
│  ├── PCI DSS (Payments)                                    │
│  └── ISO 27001                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

Authentication & Authorization

// lib/auth/config.ts
import { NextAuthConfig } from 'next-auth';
import { PrismaAdapter } from '@auth/prisma-adapter';
import { db } from '@/lib/db';
import Google from 'next-auth/providers/google';
import Credentials from 'next-auth/providers/credentials';
import { compare } from 'bcryptjs';

export const authConfig: NextAuthConfig = {
  adapter: PrismaAdapter(db),
  providers: [
    Google({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!
    }),
    Credentials({
      credentials: {
        email: { label: 'Email', type: 'email' },
        password: { label: 'Password', type: 'password' }
      },
      async authorize(credentials) {
        if (!credentials?.email || !credentials?.password) {
          return null;
        }

        const user = await db.user.findUnique({
          where: { email: credentials.email as string },
          include: { tenant: true }
        });

        if (!user || !user.passwordHash) {
          return null;
        }

        const isValid = await compare(
          credentials.password as string,
          user.passwordHash
        );

        if (!isValid) {
          // Log failed attempt
          await logSecurityEvent('failed_login', {
            email: credentials.email,
            ip: 'unknown' // Get from request
          });
          return null;
        }

        // Check if MFA is required
        if (user.mfaEnabled) {
          return { ...user, requiresMfa: true };
        }

        return user;
      }
    })
  ],
  callbacks: {
    async jwt({ token, user }) {
      if (user) {
        token.tenantId = user.tenantId;
        token.role = user.role;
        token.permissions = await getUserPermissions(user.id);
      }
      return token;
    },
    async session({ session, token }) {
      session.user.tenantId = token.tenantId;
      session.user.role = token.role;
      session.user.permissions = token.permissions;
      return session;
    }
  },
  pages: {
    signIn: '/auth/login',
    error: '/auth/error'
  },
  session: {
    strategy: 'jwt',
    maxAge: 24 * 60 * 60 // 24 hours
  },
  events: {
    async signIn({ user, isNewUser }) {
      await logSecurityEvent('login', {
        userId: user.id,
        isNewUser
      });
    },
    async signOut({ token }) {
      await logSecurityEvent('logout', {
        userId: token.sub
      });
    }
  }
};
// lib/auth/mfa.ts
import { authenticator } from 'otplib';
import { db } from '@/lib/db';
import { encrypt, decrypt } from '@/lib/crypto';

export async function setupMFA(userId: string): Promise<{
  secret: string;
  qrCodeUrl: string;
}> {
  const secret = authenticator.generateSecret();

  // Encrypt secret before storing
  const encryptedSecret = await encrypt(secret);

  await db.user.update({
    where: { id: userId },
    data: {
      mfaSecret: encryptedSecret,
      mfaEnabled: false // Enable after verification
    }
  });

  const user = await db.user.findUnique({
    where: { id: userId },
    select: { email: true }
  });

  const otpAuthUrl = authenticator.keyuri(
    user!.email,
    'YourApp',
    secret
  );

  return {
    secret,
    qrCodeUrl: otpAuthUrl
  };
}

export async function verifyMFAToken(
  userId: string,
  token: string
): Promise<boolean> {
  const user = await db.user.findUnique({
    where: { id: userId },
    select: { mfaSecret: true }
  });

  if (!user?.mfaSecret) {
    return false;
  }

  const secret = await decrypt(user.mfaSecret);
  const isValid = authenticator.verify({ token, secret });

  if (isValid) {
    // Enable MFA if this is setup verification
    await db.user.update({
      where: { id: userId },
      data: { mfaEnabled: true }
    });
  }

  return isValid;
}

// Generate backup codes
export async function generateBackupCodes(userId: string): Promise<string[]> {
  const codes: string[] = [];

  for (let i = 0; i < 10; i++) {
    codes.push(generateSecureCode(8));
  }

  // Hash and store codes
  const hashedCodes = await Promise.all(
    codes.map(code => hashCode(code))
  );

  await db.mfaBackupCode.createMany({
    data: hashedCodes.map(hash => ({
      userId,
      codeHash: hash,
      used: false
    }))
  });

  return codes; // Return plain codes to show user once
}

Role-Based Access Control

// lib/auth/rbac.ts
export const permissions = {
  // Project permissions
  'project:read': 'View projects',
  'project:create': 'Create projects',
  'project:update': 'Update projects',
  'project:delete': 'Delete projects',

  // User management
  'user:read': 'View users',
  'user:invite': 'Invite users',
  'user:update': 'Update users',
  'user:delete': 'Remove users',

  // Billing
  'billing:read': 'View billing',
  'billing:manage': 'Manage billing',

  // Admin
  'admin:settings': 'Manage settings',
  'admin:audit': 'View audit logs'
} as const;

export type Permission = keyof typeof permissions;

export const roles = {
  owner: {
    name: 'Owner',
    permissions: Object.keys(permissions) as Permission[]
  },
  admin: {
    name: 'Admin',
    permissions: [
      'project:read', 'project:create', 'project:update', 'project:delete',
      'user:read', 'user:invite', 'user:update',
      'billing:read', 'admin:settings'
    ] as Permission[]
  },
  member: {
    name: 'Member',
    permissions: [
      'project:read', 'project:create', 'project:update',
      'user:read'
    ] as Permission[]
  },
  viewer: {
    name: 'Viewer',
    permissions: ['project:read', 'user:read'] as Permission[]
  }
} as const;

export type Role = keyof typeof roles;

// Permission check middleware
export function requirePermission(permission: Permission) {
  return async (req: Request) => {
    const session = await getServerSession();

    if (!session?.user) {
      throw new Error('Unauthorized');
    }

    const userPermissions = session.user.permissions || [];

    if (!userPermissions.includes(permission)) {
      await logSecurityEvent('permission_denied', {
        userId: session.user.id,
        permission,
        resource: req.url
      });
      throw new Error('Forbidden');
    }
  };
}

// React hook for permission checks
export function usePermission(permission: Permission): boolean {
  const { data: session } = useSession();
  return session?.user?.permissions?.includes(permission) ?? false;
}

Data Encryption

// lib/crypto/encryption.ts
import { createCipheriv, createDecipheriv, randomBytes, scrypt } from 'crypto';
import { promisify } from 'util';

const scryptAsync = promisify(scrypt);

const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32;
const IV_LENGTH = 16;
const AUTH_TAG_LENGTH = 16;

// Derive key from master key
async function deriveKey(masterKey: string, salt: Buffer): Promise<Buffer> {
  return scryptAsync(masterKey, salt, KEY_LENGTH) as Promise<Buffer>;
}

export async function encrypt(plaintext: string): Promise<string> {
  const masterKey = process.env.ENCRYPTION_KEY!;
  const salt = randomBytes(16);
  const key = await deriveKey(masterKey, salt);
  const iv = randomBytes(IV_LENGTH);

  const cipher = createCipheriv(ALGORITHM, key, iv);
  const encrypted = Buffer.concat([
    cipher.update(plaintext, 'utf8'),
    cipher.final()
  ]);
  const authTag = cipher.getAuthTag();

  // Combine: salt + iv + authTag + encrypted
  const combined = Buffer.concat([salt, iv, authTag, encrypted]);
  return combined.toString('base64');
}

export async function decrypt(ciphertext: string): Promise<string> {
  const masterKey = process.env.ENCRYPTION_KEY!;
  const combined = Buffer.from(ciphertext, 'base64');

  // Extract components
  const salt = combined.subarray(0, 16);
  const iv = combined.subarray(16, 32);
  const authTag = combined.subarray(32, 48);
  const encrypted = combined.subarray(48);

  const key = await deriveKey(masterKey, salt);

  const decipher = createDecipheriv(ALGORITHM, key, iv);
  decipher.setAuthTag(authTag);

  const decrypted = Buffer.concat([
    decipher.update(encrypted),
    decipher.final()
  ]);

  return decrypted.toString('utf8');
}

// Field-level encryption for sensitive data
export class EncryptedField {
  static async encrypt(value: string): Promise<string> {
    return encrypt(value);
  }

  static async decrypt(value: string): Promise<string> {
    return decrypt(value);
  }
}

// Example usage in Prisma
// middleware for automatic encryption
export function encryptionMiddleware() {
  return async (params: any, next: any) => {
    // Define fields that need encryption
    const encryptedFields: Record<string, string[]> = {
      User: ['ssn', 'taxId'],
      PaymentMethod: ['cardNumber', 'cvv'],
      Integration: ['accessToken', 'refreshToken']
    };

    const fields = encryptedFields[params.model];
    if (!fields) return next(params);

    // Encrypt on create/update
    if (['create', 'update', 'upsert'].includes(params.action)) {
      for (const field of fields) {
        if (params.args.data?.[field]) {
          params.args.data[field] = await encrypt(params.args.data[field]);
        }
      }
    }

    const result = await next(params);

    // Decrypt on read
    if (['findUnique', 'findFirst', 'findMany'].includes(params.action) && result) {
      const items = Array.isArray(result) ? result : [result];
      for (const item of items) {
        for (const field of fields) {
          if (item[field]) {
            item[field] = await decrypt(item[field]);
          }
        }
      }
    }

    return result;
  };
}

Audit Logging

// lib/security/audit.ts
import { db } from '@/lib/db';
import { headers } from 'next/headers';

interface AuditEvent {
  action: string;
  userId?: string;
  tenantId?: string;
  resourceType?: string;
  resourceId?: string;
  details?: Record<string, unknown>;
  ip?: string;
  userAgent?: string;
}

export async function logSecurityEvent(
  action: string,
  details: Record<string, unknown> = {}
): Promise<void> {
  const headersList = await headers();

  const event: AuditEvent = {
    action,
    details,
    ip: headersList.get('x-forwarded-for') || headersList.get('x-real-ip') || 'unknown',
    userAgent: headersList.get('user-agent') || 'unknown'
  };

  await db.auditLog.create({
    data: {
      ...event,
      details: event.details,
      createdAt: new Date()
    }
  });

  // For critical events, also alert
  if (isCriticalEvent(action)) {
    await sendSecurityAlert(event);
  }
}

function isCriticalEvent(action: string): boolean {
  const criticalActions = [
    'failed_login_attempt',
    'permission_denied',
    'data_export',
    'user_deleted',
    'api_key_created',
    'mfa_disabled',
    'suspicious_activity'
  ];

  return criticalActions.includes(action);
}

// Audit log query for compliance
export async function getAuditLogs(
  tenantId: string,
  options: {
    startDate?: Date;
    endDate?: Date;
    action?: string;
    userId?: string;
    limit?: number;
    offset?: number;
  }
) {
  return db.auditLog.findMany({
    where: {
      tenantId,
      ...(options.action && { action: options.action }),
      ...(options.userId && { userId: options.userId }),
      createdAt: {
        ...(options.startDate && { gte: options.startDate }),
        ...(options.endDate && { lte: options.endDate })
      }
    },
    orderBy: { createdAt: 'desc' },
    take: options.limit || 100,
    skip: options.offset || 0
  });
}

// Data retention (GDPR compliance)
export async function purgeOldAuditLogs(retentionDays: number = 365): Promise<number> {
  const cutoffDate = new Date();
  cutoffDate.setDate(cutoffDate.getDate() - retentionDays);

  const result = await db.auditLog.deleteMany({
    where: {
      createdAt: { lt: cutoffDate }
    }
  });

  return result.count;
}

GDPR Compliance

// lib/gdpr/data-subject.ts
import { db } from '@/lib/db';
import { encrypt } from '@/lib/crypto';

// Data export (Right to Portability)
export async function exportUserData(userId: string): Promise<object> {
  const user = await db.user.findUnique({
    where: { id: userId },
    include: {
      profile: true,
      projects: true,
      activities: true,
      preferences: true
    }
  });

  if (!user) {
    throw new Error('User not found');
  }

  // Remove internal fields
  const exportData = {
    personalInfo: {
      email: user.email,
      name: user.name,
      createdAt: user.createdAt
    },
    profile: user.profile,
    projects: user.projects.map(p => ({
      name: p.name,
      createdAt: p.createdAt
    })),
    preferences: user.preferences,
    activityLog: user.activities.slice(0, 1000) // Limit
  };

  // Log the export
  await logSecurityEvent('data_export', {
    userId,
    dataTypes: Object.keys(exportData)
  });

  return exportData;
}

// Data deletion (Right to Erasure)
export async function deleteUserData(
  userId: string,
  options: { hardDelete?: boolean } = {}
): Promise<void> {
  const user = await db.user.findUnique({
    where: { id: userId }
  });

  if (!user) {
    throw new Error('User not found');
  }

  if (options.hardDelete) {
    // Permanent deletion
    await db.$transaction([
      db.activity.deleteMany({ where: { userId } }),
      db.project.deleteMany({ where: { ownerId: userId } }),
      db.profile.delete({ where: { userId } }),
      db.user.delete({ where: { id: userId } })
    ]);
  } else {
    // Soft delete with anonymization
    await db.$transaction([
      db.user.update({
        where: { id: userId },
        data: {
          email: `deleted_${userId}@anonymized.local`,
          name: 'Deleted User',
          deletedAt: new Date(),
          passwordHash: null,
          mfaSecret: null
        }
      }),
      db.profile.update({
        where: { userId },
        data: {
          bio: null,
          avatar: null,
          phone: null
        }
      })
    ]);
  }

  await logSecurityEvent('user_deleted', {
    userId,
    hardDelete: options.hardDelete
  });
}

// Consent management
export async function updateConsent(
  userId: string,
  consents: {
    marketing?: boolean;
    analytics?: boolean;
    thirdParty?: boolean;
  }
): Promise<void> {
  await db.userConsent.upsert({
    where: { userId },
    create: {
      userId,
      ...consents,
      updatedAt: new Date()
    },
    update: {
      ...consents,
      updatedAt: new Date()
    }
  });

  await logSecurityEvent('consent_updated', {
    userId,
    consents
  });
}

Security Checklist

Category Requirement Priority
Auth MFA for admins Critical
Auth SSO/SAML support Important
Data Encryption at rest Critical
Data Encryption in transit Critical
Access RBAC implementation Critical
Audit Comprehensive logging Critical
GDPR Data export capability Required
GDPR Data deletion process Required
Network WAF configured Important
Secrets Secure key management Critical

Fazit

SaaS Security erfordert:

  1. Defense in Depth: Mehrere Sicherheitsebenen
  2. Compliance: SOC 2, GDPR von Anfang an
  3. Encryption: Daten at rest und in transit
  4. Audit Trails: Vollständige Nachverfolgbarkeit

Security ist kein Feature, sondern Grundvoraussetzung.


Bildprompts

  1. "Security architecture layers diagram, defense in depth visualization"
  2. "SOC 2 compliance dashboard, audit controls checklist"
  3. "Encryption key management flow, secure vault concept"

Quellen