Menu
Nazad na Blog
2 min read
SaaS

SaaS Security & Compliance

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

SaaS SecuritySOC 2GDPRData EncryptionAuthenticationCompliance
SaaS Security & Compliance

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

CategoryRequirementPriority
**Auth**MFA for adminsCritical
**Auth**SSO/SAML supportImportant
**Data**Encryption at restCritical
**Data**Encryption in transitCritical
**Access**RBAC implementationCritical
**Audit**Comprehensive loggingCritical
**GDPR**Data export capabilityRequired
**GDPR**Data deletion processRequired
**Network**WAF configuredImportant
**Secrets**Secure key managementCritical

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