Initial commit: TimeClock v3 - Node.js/Vue.js Zeiterfassung
Features: - Backend: Node.js/Express mit MySQL/MariaDB - Frontend: Vue.js 3 mit Composition API - UTC-Zeithandling für korrekte Zeiterfassung - Timewish-basierte Überstundenberechnung - Wochenübersicht mit Urlaubs-/Krankheits-/Feiertagshandling - Bereinigtes Arbeitsende (Generell/Woche) - Überstunden-Offset für historische Daten - Fixed Layout mit scrollbarem Content - Kompakte UI mit grünem Theme
This commit is contained in:
164
backend/ID_HASHING.md
Normal file
164
backend/ID_HASHING.md
Normal file
@@ -0,0 +1,164 @@
|
||||
# ID-Hashing System
|
||||
|
||||
## Übersicht
|
||||
|
||||
Das TimeClock Backend verwendet ein automatisches ID-Hashing-System, das alle numerischen IDs in API-Responses verschlüsselt und eingehende Hash-IDs automatisch entschlüsselt.
|
||||
|
||||
## Warum ID-Hashing?
|
||||
|
||||
- **Sicherheit**: Verhindert, dass Angreifer die Anzahl der Datensätze erraten können
|
||||
- **Obfuscation**: Versteckt die interne Datenbankstruktur
|
||||
- **Schutz vor ID-Enumeration**: Verhindert systematisches Durchlaufen von Ressourcen
|
||||
|
||||
## Funktionsweise
|
||||
|
||||
### Automatisches Hashing (Backend → Frontend)
|
||||
|
||||
Alle numerischen ID-Felder in API-Responses werden automatisch in Hashes konvertiert:
|
||||
|
||||
```javascript
|
||||
// Datenbank-Daten:
|
||||
{
|
||||
id: 123,
|
||||
user_id: 456,
|
||||
full_name: "Max Mustermann"
|
||||
}
|
||||
|
||||
// API-Response:
|
||||
{
|
||||
id: "xY9kL2mP3qR5.aB7cD8eF9gH0",
|
||||
user_id: "tU6vW7xY8zZ9.iJ1kL2mN3oP4",
|
||||
full_name: "Max Mustermann"
|
||||
}
|
||||
```
|
||||
|
||||
### Automatisches Enthashen (Frontend → Backend)
|
||||
|
||||
Alle Hash-IDs in eingehenden Requests werden automatisch zurück in numerische IDs konvertiert:
|
||||
|
||||
```javascript
|
||||
// Frontend sendet:
|
||||
{
|
||||
user_id: "xY9kL2mP3qR5.aB7cD8eF9gH0"
|
||||
}
|
||||
|
||||
// Backend erhält:
|
||||
{
|
||||
user_id: 123
|
||||
}
|
||||
```
|
||||
|
||||
## Implementierung
|
||||
|
||||
### Backend
|
||||
|
||||
Das System besteht aus drei Komponenten:
|
||||
|
||||
1. **`utils/hashId.js`**: Utility-Klasse für Encoding/Decoding
|
||||
2. **`middleware/hashResponse.js`**: Middleware für ausgehende Responses
|
||||
3. **`middleware/unhashRequest.js`**: Middleware für eingehende Requests
|
||||
|
||||
### Konfiguration
|
||||
|
||||
In der `.env`-Datei:
|
||||
|
||||
```env
|
||||
HASH_ID_SECRET=your-hash-id-secret-change-in-production
|
||||
```
|
||||
|
||||
⚠️ **Wichtig**: Das Secret sollte in Produktion geändert werden und geheim bleiben!
|
||||
|
||||
### Erkannte ID-Felder
|
||||
|
||||
Folgende Feldnamen werden automatisch als IDs erkannt und gehashed:
|
||||
|
||||
- `id`, `_id`
|
||||
- `user_id`, `userId`
|
||||
- `auth_info_id`, `authInfoId`
|
||||
- `auth_token_id`, `authTokenId`
|
||||
- `worklog_id`, `worklogId`
|
||||
- `vacation_id`, `vacationId`
|
||||
- `sick_id`, `sickId`
|
||||
- `holiday_id`, `holidayId`
|
||||
- `state_id`, `stateId`
|
||||
- `sick_type_id`, `sickTypeId`
|
||||
- `weekly_worktime_id`, `weeklyWorktimeId`
|
||||
|
||||
## Frontend-Integration
|
||||
|
||||
Das Frontend muss keine Änderungen vornehmen - es arbeitet einfach mit den empfangenen Hash-IDs:
|
||||
|
||||
```javascript
|
||||
// GET /api/auth/me
|
||||
const response = await fetch('/api/auth/me')
|
||||
const data = await response.json()
|
||||
|
||||
console.log(data.user.id) // "xY9kL2mP3qR5.aB7cD8eF9gH0"
|
||||
|
||||
// POST /api/some-endpoint
|
||||
await fetch('/api/some-endpoint', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
user_id: data.user.id // Hash wird automatisch entschlüsselt
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
## Manuelle Verwendung
|
||||
|
||||
Falls manuelles Encoding/Decoding nötig ist:
|
||||
|
||||
```javascript
|
||||
const hashId = require('./utils/hashId');
|
||||
|
||||
// Einzelne ID hashen
|
||||
const hash = hashId.encode(123); // "xY9kL2mP3qR5.aB7cD8eF9gH0"
|
||||
|
||||
// Hash dekodieren
|
||||
const id = hashId.decode(hash); // 123
|
||||
|
||||
// Objekt hashen
|
||||
const obj = { id: 123, name: "Test" };
|
||||
const hashed = hashId.encodeObject(obj); // { id: "xY9...", name: "Test" }
|
||||
|
||||
// Array hashen
|
||||
const array = [{ id: 1 }, { id: 2 }];
|
||||
const hashedArray = hashId.encodeArray(array);
|
||||
```
|
||||
|
||||
## Sicherheitshinweise
|
||||
|
||||
1. **Secret ändern**: Ändern Sie `HASH_ID_SECRET` in der Produktion
|
||||
2. **Secret sicher aufbewahren**: Das Secret sollte niemals im Code oder in der Versionskontrolle erscheinen
|
||||
3. **Keine zusätzliche Sicherheit**: ID-Hashing ersetzt keine echte Autorisierung - prüfen Sie immer die Zugriffsrechte!
|
||||
|
||||
## Hash-Format
|
||||
|
||||
Das Hash-Format: `{encrypted_id}.{hash_prefix}`
|
||||
|
||||
- **encrypted_id**: AES-256-CBC verschlüsselte ID
|
||||
- **hash_prefix**: HMAC-SHA256 Hash (erste 12 Zeichen) zur Verifizierung
|
||||
- **Encoding**: base64url (URL-sicher)
|
||||
|
||||
Beispiel: `xY9kL2mP3qR5.aB7cD8eF9gH0`
|
||||
|
||||
## Fehlerbehandlung
|
||||
|
||||
Ungültige Hash-IDs werden zu `null` dekodiert. Services/Controller sollten dies behandeln:
|
||||
|
||||
```javascript
|
||||
const userId = req.params.id; // Könnte null sein wenn Hash ungültig
|
||||
|
||||
if (!userId) {
|
||||
return res.status(400).json({ error: 'Ungültige ID' });
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
- **Encoding**: ~0.1ms pro ID
|
||||
- **Decoding**: ~0.2ms pro ID
|
||||
- **Overhead**: Minimal, da deterministisch und ohne Datenbank-Zugriff
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user