Files
Portfolio/blog-posts/90-saas-security-compliance.md
damjan_savicandClaude Opus 4.5 e1bbe5455d Initial commit: Portfolio Website
Vollständige Next.js 15 Portfolio-Website mit:
- Blog-System mit 100+ Artikeln
- Supabase-Integration
- Responsive Design mit Tailwind CSS
- TypeScript-Konfiguration
- Testing-Setup mit Vitest und Playwright

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-01 15:07:20 +01:00

735 lines
20 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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<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
```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<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
```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<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
```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<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
- [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/)