# 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 ```typescript // 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 }); } } }; ``` ```typescript // 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 { 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 { 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 ```typescript // 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 ```typescript // 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 { return scryptAsync(masterKey, salt, KEY_LENGTH) as Promise; } export async function encrypt(plaintext: string): Promise { 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 { 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 { return encrypt(value); } static async decrypt(value: string): Promise { 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 = { 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 ```typescript // 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; ip?: string; userAgent?: string; } export async function logSecurityEvent( action: string, details: Record = {} ): Promise { 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 { const cutoffDate = new Date(); cutoffDate.setDate(cutoffDate.getDate() - retentionDays); const result = await db.auditLog.deleteMany({ where: { createdAt: { lt: cutoffDate } } }); return result.count; } ``` --- ## GDPR Compliance ```typescript // 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 { 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 { 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 { 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 - [SOC 2 Compliance Guide](https://www.aicpa.org/resources/soc-2) - [GDPR Official Text](https://gdpr.eu/) - [OWASP Security Guidelines](https://owasp.org/www-project-web-security-testing-guide/) - [AWS Security Best Practices](https://aws.amazon.com/security/security-resources/)