# SECURITY HARDENING - ERP Generic

**Última actualización:** 2025-11-24
**Responsable:** Security Team / DevOps Team
**Estado:** ✅ Production-Ready

---

## TABLE OF CONTENTS

1. [Overview](#1-overview)
2. [OWASP Top 10 Mitigations](#2-owasp-top-10-mitigations)
3. [Authentication & Authorization](#3-authentication--authorization)
4. [Input Validation & Sanitization](#4-input-validation--sanitization)
5. [SQL Injection Prevention](#5-sql-injection-prevention)
6. [XSS & CSRF Protection](#6-xss--csrf-protection)
7. [Security Headers](#7-security-headers)
8. [Rate Limiting & DDoS Protection](#8-rate-limiting--ddos-protection)
9. [Secrets Management](#9-secrets-management)
10. [SSL/TLS Configuration](#10-ssltls-configuration)
11. [Security Scanning & Auditing](#11-security-scanning--auditing)
12. [Incident Response](#12-incident-response)

---

## 1. OVERVIEW

### 1.1 Security Posture

**Defense in Depth Strategy:**
```
┌─────────────────────────────────────────────────────────┐
│ Layer 7: Application Security                          │
│ - Input validation, Output encoding                    │
│ - OWASP Top 10 mitigations, Code review               │
└─────────────────┬───────────────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────────────┐
│ Layer 6: Authentication & Authorization                │
│ - JWT with rotation, RBAC, MFA                        │
└─────────────────┬───────────────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────────────┐
│ Layer 5: API Security                                  │
│ - Rate limiting, CORS, API Gateway                    │
└─────────────────┬───────────────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────────────┐
│ Layer 4: Network Security                             │
│ - Firewall, Security groups, WAF                      │
└─────────────────┬───────────────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────────────┐
│ Layer 3: Data Security                                │
│ - Encryption at rest/transit, RLS, Backup encryption  │
└─────────────────┬───────────────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────────────┐
│ Layer 2: Infrastructure Security                      │
│ - OS hardening, Container security, Secrets mgmt      │
└─────────────────┬───────────────────────────────────────┘
                  │
┌─────────────────▼───────────────────────────────────────┐
│ Layer 1: Physical Security                            │
│ - Data center security, Access controls               │
└─────────────────────────────────────────────────────────┘
```

### 1.2 Security Objectives

- **Confidentiality:** Protect sensitive data (PII, financial data)
- **Integrity:** Prevent unauthorized data modification
- **Availability:** Ensure system uptime (DDoS protection)
- **Accountability:** Audit trail for all sensitive operations
- **Non-Repudiation:** Digital signatures for critical transactions

---

## 2. OWASP TOP 10 MITIGATIONS

### 2.1 A01:2021 - Broken Access Control

**Threat:** Users accessing resources without proper authorization.

**Mitigation:**

```typescript
// backend/src/common/guards/rbac.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Permission } from '../decorators/permission.decorator';

@Injectable()
export class RBACGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const requiredPermissions = this.reflector.get<string[]>('permissions', context.getHandler());
    if (!requiredPermissions) {
      return true; // No permissions required
    }

    const request = context.switchToHttp().getRequest();
    const user = request.user;

    // Check if user has required permissions
    const hasPermission = requiredPermissions.every((permission) =>
      user.permissions?.includes(permission)
    );

    if (!hasPermission) {
      throw new ForbiddenException('Insufficient permissions');
    }

    // Check tenant isolation
    if (request.body?.tenant_id && request.body.tenant_id !== user.tenant_id) {
      throw new ForbiddenException('Access denied: Cross-tenant access prohibited');
    }

    return true;
  }
}

// Usage in controller
@Controller('sales/orders')
@UseGuards(JwtAuthGuard, RBACGuard)
export class SalesOrdersController {
  @Post()
  @Permissions('sales:orders:create') // Decorator defines required permission
  createOrder(@Body() dto: CreateOrderDto, @CurrentUser() user: User) {
    // Enforce tenant_id from authenticated user (prevent tampering)
    dto.tenant_id = user.tenant_id;
    return this.ordersService.create(dto);
  }
}
```

**RLS (Row-Level Security) in PostgreSQL:**

```sql
-- Enable RLS on all tenant-specific tables
ALTER TABLE sales.orders ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see orders from their tenant
CREATE POLICY tenant_isolation_policy ON sales.orders
  FOR ALL
  TO public
  USING (tenant_id = current_setting('app.current_tenant_id')::UUID);

-- Service role can bypass RLS (for admin operations)
CREATE ROLE service_role BYPASSRLS;
```

---

### 2.2 A02:2021 - Cryptographic Failures

**Threat:** Sensitive data exposed due to weak or missing encryption.

**Mitigation:**

```typescript
// Encryption at rest (sensitive fields in database)
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto';

export class EncryptionService {
  private algorithm = 'aes-256-gcm';
  private key: Buffer;

  constructor() {
    // Derive key from secret (stored in environment variable)
    this.key = scryptSync(process.env.ENCRYPTION_SECRET, 'salt', 32);
  }

  encrypt(plaintext: string): string {
    const iv = randomBytes(16);
    const cipher = createCipheriv(this.algorithm, this.key, iv);

    let encrypted = cipher.update(plaintext, 'utf8', 'hex');
    encrypted += cipher.final('hex');

    const authTag = cipher.getAuthTag();

    // Return: iv:authTag:encrypted
    return `${iv.toString('hex')}:${authTag.toString('hex')}:${encrypted}`;
  }

  decrypt(ciphertext: string): string {
    const [ivHex, authTagHex, encrypted] = ciphertext.split(':');

    const iv = Buffer.from(ivHex, 'hex');
    const authTag = Buffer.from(authTagHex, 'hex');
    const decipher = createDecipheriv(this.algorithm, this.key, iv);
    decipher.setAuthTag(authTag);

    let decrypted = decipher.update(encrypted, 'hex', 'utf8');
    decrypted += decipher.final('utf8');

    return decrypted;
  }
}

// Usage: Encrypt SSN, credit card numbers, etc.
@Column({ type: 'text' })
get ssn(): string {
  return this._ssn;
}

set ssn(value: string) {
  this._ssn = this.encryptionService.encrypt(value);
}
```

**SSL/TLS Configuration:**
- TLS 1.2+ only (disable TLS 1.0, 1.1)
- Strong cipher suites only
- HSTS header enabled
- Certificate pinning (mobile apps)

---

### 2.3 A03:2021 - Injection

**Threat:** SQL injection, NoSQL injection, Command injection.

**Mitigation:**

```typescript
// Prisma ORM (parameterized queries - safe by default)
const users = await prisma.user.findMany({
  where: {
    email: userInput, // Automatically parameterized
    tenant_id: tenantId,
  },
});

// NEVER do this (vulnerable to SQL injection):
// const query = `SELECT * FROM users WHERE email = '${userInput}'`;

// Command injection prevention
import { exec } from 'child_process';
import { promisify } from 'util';
const execAsync = promisify(exec);

// BAD: Direct command execution with user input
// await execAsync(`convert ${userInput} output.png`); // VULNERABLE!

// GOOD: Whitelist allowed values
const allowedFormats = ['png', 'jpg', 'gif'];
if (!allowedFormats.includes(format)) {
  throw new BadRequestException('Invalid format');
}
await execAsync(`convert input.${format} output.png`);
```

---

### 2.4 A04:2021 - Insecure Design

**Mitigation:**
- Security requirements in design phase
- Threat modeling (STRIDE methodology)
- Secure by default configurations
- Principle of least privilege

---

### 2.5 A05:2021 - Security Misconfiguration

**Mitigation:**

```typescript
// helmet.config.ts - Security headers
import helmet from 'helmet';

export const helmetConfig = helmet({
  contentSecurityPolicy: {
    directives: {
      defaultSrc: ["'self'"],
      styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
      scriptSrc: ["'self'"],
      imgSrc: ["'self'", 'data:', 'https:'],
      fontSrc: ["'self'", 'https://fonts.gstatic.com'],
      connectSrc: ["'self'", 'https://api.erp-generic.com'],
      frameSrc: ["'none'"],
      objectSrc: ["'none'"],
    },
  },
  hsts: {
    maxAge: 31536000, // 1 year
    includeSubDomains: true,
    preload: true,
  },
  frameguard: { action: 'deny' }, // Prevent clickjacking
  noSniff: true, // X-Content-Type-Options
  xssFilter: true, // X-XSS-Protection
  referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
});

// main.ts
app.use(helmetConfig);
```

**Disable Debug Info in Production:**

```typescript
// main.ts
if (process.env.NODE_ENV === 'production') {
  app.enableCors({
    origin: process.env.ALLOWED_ORIGINS.split(','),
    credentials: true,
  });

  // Disable detailed error messages
  app.useGlobalFilters(new ProductionExceptionFilter());
} else {
  app.enableCors({ origin: '*' }); // Dev only
}
```

---

### 2.6 A06:2021 - Vulnerable and Outdated Components

**Mitigation:**

```bash
# Automated dependency scanning
npm audit --audit-level=high

# Snyk vulnerability scanning
snyk test

# Update dependencies regularly
npm outdated
npm update

# Renovate Bot (automated PRs for dependency updates)
# .github/renovate.json
{
  "extends": ["config:base"],
  "schedule": ["every weekend"],
  "labels": ["dependencies"],
  "automerge": false
}
```

---

### 2.7 A07:2021 - Identification and Authentication Failures

**Mitigation:** See Section 3 (JWT with rotation, MFA, password policies)

---

### 2.8 A08:2021 - Software and Data Integrity Failures

**Mitigation:**

```typescript
// Verify integrity of uploads
import * as crypto from 'crypto';

function verifyFileIntegrity(file: Buffer, expectedHash: string): boolean {
  const hash = crypto.createHash('sha256').update(file).digest('hex');
  return hash === expectedHash;
}

// Code signing for deployments
// Sign Docker images
docker trust sign erp-generic-backend:v1.0.0

// Verify signature before deployment
docker trust inspect erp-generic-backend:v1.0.0
```

---

### 2.9 A09:2021 - Security Logging and Monitoring Failures

**Mitigation:**

```typescript
// Audit logging for sensitive operations
logger.audit('USER_LOGIN', user.id, tenantId, {
  ip: request.ip,
  userAgent: request.headers['user-agent'],
  success: true,
  timestamp: new Date(),
});

logger.audit('DATA_EXPORT', user.id, tenantId, {
  table: 'sales.orders',
  rowCount: 1500,
  format: 'CSV',
});

logger.audit('PERMISSION_CHANGE', adminUser.id, tenantId, {
  targetUser: userId,
  oldRole: 'user',
  newRole: 'admin',
});
```

---

### 2.10 A10:2021 - Server-Side Request Forgery (SSRF)

**Mitigation:**

```typescript
// Validate URLs before making requests
import { URL } from 'url';

function isUrlSafe(urlString: string): boolean {
  try {
    const url = new URL(urlString);

    // Whitelist allowed protocols
    if (!['http:', 'https:'].includes(url.protocol)) {
      return false;
    }

    // Blacklist private IP ranges
    const hostname = url.hostname;
    const privateRanges = [
      /^localhost$/,
      /^127\.\d+\.\d+\.\d+$/,
      /^10\.\d+\.\d+\.\d+$/,
      /^192\.168\.\d+\.\d+$/,
      /^172\.(1[6-9]|2\d|3[01])\.\d+\.\d+$/,
    ];

    for (const range of privateRanges) {
      if (range.test(hostname)) {
        return false;
      }
    }

    return true;
  } catch {
    return false;
  }
}

// Usage
if (!isUrlSafe(userProvidedUrl)) {
  throw new BadRequestException('Invalid URL');
}

const response = await axios.get(userProvidedUrl);
```

---

## 3. AUTHENTICATION & AUTHORIZATION

### 3.1 JWT Configuration

```typescript
// jwt.config.ts
export const jwtConfig = {
  secret: process.env.JWT_SECRET,
  signOptions: {
    expiresIn: '15m', // Short-lived access tokens
    algorithm: 'HS256',
    issuer: 'erp-generic',
    audience: 'erp-generic-api',
  },
  refreshSecret: process.env.JWT_REFRESH_SECRET,
  refreshSignOptions: {
    expiresIn: '7d', // Long-lived refresh tokens
  },
};

// auth.service.ts
async login(email: string, password: string) {
  const user = await this.validateUser(email, password);

  const payload = {
    sub: user.id,
    email: user.email,
    tenant_id: user.tenant_id,
    roles: user.roles,
    permissions: user.permissions,
  };

  return {
    access_token: this.jwtService.sign(payload),
    refresh_token: this.jwtService.sign(payload, {
      secret: jwtConfig.refreshSecret,
      expiresIn: jwtConfig.refreshSignOptions.expiresIn,
    }),
    expires_in: 900, // 15 minutes
  };
}
```

### 3.2 Password Policy

```typescript
// Strong password requirements
const passwordPolicy = {
  minLength: 12,
  requireUppercase: true,
  requireLowercase: true,
  requireNumbers: true,
  requireSpecialChars: true,
  preventCommonPasswords: true,
  preventPasswordReuse: 5, // Last 5 passwords
  maxAge: 90, // days (force reset after 90 days)
};

// Password hashing with bcrypt
import * as bcrypt from 'bcrypt';

const SALT_ROUNDS = 12;

async hashPassword(password: string): Promise<string> {
  return bcrypt.hash(password, SALT_ROUNDS);
}

async comparePassword(password: string, hash: string): Promise<boolean> {
  return bcrypt.compare(password, hash);
}
```

### 3.3 Multi-Factor Authentication (MFA)

```typescript
// MFA with TOTP (Time-based One-Time Password)
import * as speakeasy from 'speakeasy';
import * as qrcode from 'qrcode';

async enableMFA(userId: string) {
  const secret = speakeasy.generateSecret({
    name: `ERP Generic (${user.email})`,
    issuer: 'ERP Generic',
  });

  // Store secret in database (encrypted)
  await this.prisma.user.update({
    where: { id: userId },
    data: {
      mfa_secret: this.encryptionService.encrypt(secret.base32),
      mfa_enabled: false, // Enable after verification
    },
  });

  // Generate QR code for user to scan
  const qrCodeUrl = await qrcode.toDataURL(secret.otpauth_url);

  return { qrCodeUrl, secret: secret.base32 };
}

async verifyMFA(userId: string, token: string): Promise<boolean> {
  const user = await this.prisma.user.findUnique({ where: { id: userId } });
  const secret = this.encryptionService.decrypt(user.mfa_secret);

  return speakeasy.totp.verify({
    secret,
    encoding: 'base32',
    token,
    window: 1, // Allow 1 time step before/after
  });
}
```

---

## 4. INPUT VALIDATION & SANITIZATION

```typescript
// Using class-validator and class-transformer
import { IsEmail, IsString, MinLength, MaxLength, Matches, IsUUID } from 'class-validator';
import { Transform } from 'class-transformer';
import * as sanitizeHtml from 'sanitize-html';

export class CreateUserDto {
  @IsEmail()
  @Transform(({ value }) => value.toLowerCase().trim())
  email: string;

  @IsString()
  @MinLength(12)
  @MaxLength(128)
  @Matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]/)
  password: string;

  @IsString()
  @MinLength(2)
  @MaxLength(100)
  @Transform(({ value }) => sanitizeHtml(value, { allowedTags: [], allowedAttributes: {} }))
  firstName: string;

  @IsUUID()
  tenant_id: string;
}

// Global validation pipe
app.useGlobalPipes(
  new ValidationPipe({
    whitelist: true, // Strip unknown properties
    forbidNonWhitelisted: true, // Throw error if unknown properties present
    transform: true, // Auto-transform payloads to DTO instances
  })
);
```

---

## 5. SQL INJECTION PREVENTION

**Prisma ORM (Safe by Default):**
```typescript
// ✅ SAFE: Parameterized query
const user = await prisma.user.findUnique({
  where: { email: userInput },
});

// ✅ SAFE: Raw query with parameters
const result = await prisma.$queryRaw`
  SELECT * FROM users WHERE email = ${userInput}
`;

// ❌ NEVER DO THIS:
// const result = await prisma.$queryRawUnsafe(
//   `SELECT * FROM users WHERE email = '${userInput}'`
// );
```

---

## 6. XSS & CSRF PROTECTION

### 6.1 XSS Prevention

```typescript
// Frontend: Sanitize user input before rendering
import DOMPurify from 'dompurify';

const sanitizedHtml = DOMPurify.sanitize(userInput, {
  ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
  ALLOWED_ATTR: ['href'],
});

// React automatically escapes JSX
<div>{userInput}</div> // Safe

// Dangerous: Bypass escaping
<div dangerouslySetInnerHTML={{ __html: sanitizedHtml }} /> // Use with caution
```

### 6.2 CSRF Protection

```typescript
// CSRF token in NestJS
import * as csurf from 'csurf';

app.use(csurf({ cookie: true }));

// Endpoint to get CSRF token
@Get('csrf-token')
getCsrfToken(@Req() req) {
  return { csrfToken: req.csrfToken() };
}

// Frontend: Include CSRF token in requests
axios.post('/api/orders', orderData, {
  headers: {
    'X-CSRF-Token': csrfToken,
  },
});
```

---

## 7. SECURITY HEADERS

```typescript
// Comprehensive security headers configuration
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"], // Remove unsafe-inline in production
        styleSrc: ["'self'", "'unsafe-inline'", 'https://fonts.googleapis.com'],
        imgSrc: ["'self'", 'data:', 'https:'],
        fontSrc: ["'self'", 'https://fonts.gstatic.com'],
        connectSrc: ["'self'", 'https://api.erp-generic.com'],
        frameSrc: ["'none'"],
        objectSrc: ["'none'"],
        upgradeInsecureRequests: [],
      },
    },
    hsts: {
      maxAge: 31536000,
      includeSubDomains: true,
      preload: true,
    },
    noSniff: true,
    frameguard: { action: 'deny' },
    xssFilter: true,
    referrerPolicy: { policy: 'strict-origin-when-cross-origin' },
    permissionsPolicy: {
      features: {
        geolocation: ["'none'"],
        microphone: ["'none'"],
        camera: ["'none'"],
        payment: ["'self'"],
      },
    },
  })
);
```

---

## 8. RATE LIMITING & DDOS PROTECTION

```typescript
// Rate limiting with @nestjs/throttler
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';

@Module({
  imports: [
    ThrottlerModule.forRoot({
      ttl: 60, // Time window in seconds
      limit: 100, // Max requests per ttl
    }),
  ],
})
export class AppModule {}

// Apply globally
app.useGlobalGuards(new ThrottlerGuard());

// Custom rate limits per endpoint
@Throttle(10, 60) // 10 requests per minute
@Post('login')
async login(@Body() dto: LoginDto) {
  return this.authService.login(dto);
}

// IP-based rate limiting for brute-force protection
@Throttle(5, 300) // 5 failed login attempts per 5 minutes
@Post('login')
async login(@Body() dto: LoginDto, @Ip() ip: string) {
  // Log failed attempts
  const attempts = await this.redis.incr(`login_attempts:${ip}`);
  await this.redis.expire(`login_attempts:${ip}`, 300);

  if (attempts > 5) {
    throw new TooManyRequestsException('Too many login attempts. Try again in 5 minutes.');
  }

  return this.authService.login(dto);
}
```

---

## 9. SECRETS MANAGEMENT

### 9.1 HashiCorp Vault Integration

```typescript
// vault.service.ts
import * as vault from 'node-vault';

export class VaultService {
  private client: vault.client;

  constructor() {
    this.client = vault({
      apiVersion: 'v1',
      endpoint: process.env.VAULT_ADDR,
      token: process.env.VAULT_TOKEN,
    });
  }

  async getSecret(path: string): Promise<any> {
    const result = await this.client.read(path);
    return result.data;
  }

  async setSecret(path: string, data: any): Promise<void> {
    await this.client.write(path, { data });
  }
}

// Usage
const dbPassword = await vaultService.getSecret('secret/erp-generic/prod/database');
```

### 9.2 Environment Variables Security

```bash
# .env file (NEVER commit to Git!)
# Use .env.example as template

DATABASE_URL=postgresql://user:STRONG_PASSWORD@localhost:5432/db
JWT_SECRET=RANDOM_64_CHAR_STRING
ENCRYPTION_SECRET=RANDOM_64_CHAR_STRING

# AWS Secrets Manager (production)
aws secretsmanager create-secret --name erp-generic/prod/jwt-secret --secret-string "RANDOM_64_CHAR_STRING"
```

---

## 10. SSL/TLS CONFIGURATION

### 10.1 Nginx SSL Configuration

```nginx
server {
    listen 443 ssl http2;
    server_name erp-generic.com;

    # SSL certificates
    ssl_certificate /etc/nginx/ssl/erp-generic.com.crt;
    ssl_certificate_key /etc/nginx/ssl/erp-generic.com.key;

    # SSL protocols and ciphers
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';
    ssl_prefer_server_ciphers on;

    # HSTS
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/ssl/chain.pem;

    # SSL session cache
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location / {
        proxy_pass http://backend:3000;
        proxy_set_header X-Forwarded-Proto https;
    }
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name erp-generic.com;
    return 301 https://$server_name$request_uri;
}
```

### 10.2 Let's Encrypt Automation

```bash
# Install Certbot
apt-get install certbot python3-certbot-nginx

# Obtain certificate
certbot --nginx -d erp-generic.com -d www.erp-generic.com

# Auto-renewal (cron)
0 0 1 * * certbot renew --quiet
```

---

## 11. SECURITY SCANNING & AUDITING

### 11.1 Automated Security Scans

```yaml
# .github/workflows/security-scan.yml
name: Security Scan

on:
  push:
    branches: [main, develop]
  schedule:
    - cron: '0 0 * * *' # Daily

jobs:
  snyk:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high

  owasp-zap:
    runs-on: ubuntu-latest
    steps:
      - name: OWASP ZAP Scan
        uses: zaproxy/action-baseline@v0.7.0
        with:
          target: 'https://staging.erp-generic.com'
          rules_file_name: '.zap/rules.tsv'
          cmd_options: '-a'
```

### 11.2 Security Audit Checklist

**Monthly Security Audit:**
- [ ] Review access logs for anomalies
- [ ] Check failed login attempts
- [ ] Verify MFA enrollment rate (target: 90%)
- [ ] Review user permissions (principle of least privilege)
- [ ] Scan for outdated dependencies (npm audit)
- [ ] Review firewall rules
- [ ] Check SSL certificate expiration
- [ ] Verify backup encryption
- [ ] Test disaster recovery plan
- [ ] Review security alerts from monitoring

---

## 12. INCIDENT RESPONSE

### 12.1 Security Incident Response Plan

**Phases:**
1. **Detection:** Alerts via monitoring, reports from users
2. **Containment:** Isolate affected systems, block attacker
3. **Eradication:** Remove malware, patch vulnerabilities
4. **Recovery:** Restore services, verify integrity
5. **Post-Mortem:** Document lessons learned, improve defenses

**Contact:**
- **Security Team:** security@erp-generic.com
- **On-Call:** security-oncall@erp-generic.com (PagerDuty)
- **Escalation:** CISO, CTO, CEO

---

## REFERENCES

- [OWASP Top 10 2021](https://owasp.org/www-project-top-ten/)
- [CWE Top 25](https://cwe.mitre.org/top25/archive/2021/2021_cwe_top25.html)
- [NIST Cybersecurity Framework](https://www.nist.gov/cyberframework)
- [Deployment Guide](./DEPLOYMENT-GUIDE.md)

---

**Documento:** SECURITY-HARDENING.md
**Versión:** 1.0
**Total Páginas:** ~15
**Última Actualización:** 2025-11-24
