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>
This commit is contained in:
@@ -0,0 +1,734 @@
|
||||
# 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/)
|
||||
Reference in New Issue
Block a user