Zum Hauptinhalt springen
BiometricsMar 28, 2026

Aufbau biometrischer Verifizierungssysteme fuer Indonesien: Architektur und Rust-Patterns

OS
Open Soft Team

Engineering Team

Aufbau des Backends fuer die biometrische SIM-Verifizierung in Indonesien

Dieser Artikel ist der dritte Teil der Serie ueber Indonesiens biometrische SIM-Pflicht und konzentriert sich auf die praktischen Architekturentscheidungen und Code-Implementierungen, die fuer den Aufbau eines produktionsreifen biometrischen Verifizierungssystems erforderlich sind. Wir vertiefen uns in Systemdesign, Datenfluesse, Verschluesselungspraktiken und skalierbare Backend-Dienste, die mit Rust und Axum erstellt werden.

Systemarchitektur-Ueberblick

Ein produktionsreifes biometrisches Verifizierungssystem besteht aus mehreren miteinander verbundenen Komponenten, die jeweils bestimmte Verantwortlichkeiten haben:

Kernkomponenten

  1. Erfassungsdienst (Mobile/Web-SDK) — Frontend-Komponente fuer die Gesichtsbilderfassung und geraeteseitige Lebendigkeitserkennung
  2. API-Gateway — Zentralisierte Authentifizierung, Ratenbegrenzung, Request-Routing und TLS-Terminierung
  3. Biometrische Verarbeitungs-Engine — Dienst zur Extraktion biometrischer Merkmals-Templates aus Gesichtsbildern
  4. Lebendigkeitserkennungsdienst — Fuehrt passive und aktive Lebendigkeitserkennungsmodelle aus
  5. IKD-Integrationsdienst — Behandelt die 1:1-Verifizierungskommunikation mit der indonesischen IKD-Plattform
  6. Verschluesselungsdienst — AES-256-Schluesselverwaltung, Template-Verschluesselung/Entschluesselung
  7. Audit-Log-Dienst — Protokolliert alle Verifizierungstransaktionen fuer die regulatorische Compliance
  8. Monitoring und Alerting — Systemzustand, Leistungsmetriken und Anomalieerkennung

Datenfluss-Architektur

Client-SDK → API-Gateway → Biometrische Engine → IKD-Plattform
                ↓              ↓            ↓
        Ratenbegrenzung  Verschluesselungsdienst  Audit-Log
                ↓              ↓            ↓
        Auth-Cache      Schluesselverwaltung  Compliance-Speicher

Rust-Backend-Implementierung

Projektstruktur

biometric-service/
├── Cargo.toml
├── src/
│   ├── main.rs              # Einstiegspunkt und Server-Setup
│   ├── config.rs            # Konfigurationsverwaltung
│   ├── routes/
│   │   ├── mod.rs
│   │   ├── verify.rs        # Verifizierungsendpunkte
│   │   ├── health.rs        # Gesundheitspruefung
│   │   └── admin.rs         # Verwaltungsendpunkte
│   ├── services/
│   │   ├── mod.rs
│   │   ├── biometric.rs     # Biometrische Verarbeitung
│   │   ├── liveness.rs      # Lebendigkeitserkennung
│   │   ├── ikd.rs           # IKD-Plattform-Client
│   │   ├── crypto.rs        # Verschluesselungsoperationen
│   │   └── audit.rs         # Audit-Protokollierung
│   ├── models/
│   ├── middleware/
│   └── errors.rs
├── migrations/
└── tests/

Kern-Verifizierungsendpunkt

use axum::{extract::State, Json};
use chrono::Utc;
use uuid::Uuid;

pub async fn verify_biometric(
    State(state): State<AppState>,
    Json(req): Json<VerificationRequest>,
) -> Result<Json<VerificationResponse>, AppError> {
    let transaction_id = Uuid::new_v4();
    let started_at = Utc::now();

    req.validate()?;

    let liveness = state.liveness_service
        .detect(&req.capture_data)
        .await
        .map_err(|e| {
            state.audit.log_failure(transaction_id, "liveness_failed", &e);
            e
        })?;

    if !liveness.is_live {
        return Err(AppError::LivenessCheckFailed);
    }

    let template = state.biometric_engine
        .extract(&req.facial_image).await?;

    let encrypted = state.crypto_service
        .encrypt_template(&template).await?;

    let ikd_result = state.ikd_client
        .verify(&req.nik, &encrypted).await?;

    let elapsed = Utc::now() - started_at;
    state.audit.log_verification(AuditRecord {
        transaction_id,
        nik_hash: hash_nik(&req.nik),
        liveness_score: liveness.confidence,
        match_score: ikd_result.score,
        verified: ikd_result.matched,
        duration_ms: elapsed.num_milliseconds(),
        timestamp: started_at,
    }).await?;

    Ok(Json(VerificationResponse {
        transaction_id,
        verified: ikd_result.matched,
        confidence: ikd_result.score,
    }))
}

AES-256-Verschluesselungsdienst

use aes_gcm::{Aes256Gcm, KeyInit, Nonce};
use aes_gcm::aead::Aead;
use rand::RngCore;

pub struct CryptoService {
    cipher: Aes256Gcm,
}

impl CryptoService {
    pub fn new(key: &[u8; 32]) -> Self {
        let cipher = Aes256Gcm::new_from_slice(key)
            .expect("AES-256 key must be 32 bytes");
        Self { cipher }
    }

    pub async fn encrypt_template(
        &self,
        template: &BiometricTemplate,
    ) -> Result<EncryptedTemplate, CryptoError> {
        let mut nonce_bytes = [0u8; 12];
        rand::thread_rng().fill_bytes(&mut nonce_bytes);
        let nonce = Nonce::from_slice(&nonce_bytes);

        let plaintext = bincode::serialize(template)?;
        let ciphertext = self.cipher
            .encrypt(nonce, plaintext.as_ref())
            .map_err(|_| CryptoError::EncryptionFailed)?;

        Ok(EncryptedTemplate {
            ciphertext,
            nonce: nonce_bytes.to_vec(),
            algorithm: "AES-256-GCM".into(),
        })
    }
}

Skalierbarkeit und Leistung

Mit ueber 270 Millionen Einwohnern und 345 Millionen aktiven SIM-Karten sind die Skalierungsanforderungen enorm:

  • Spitzenlast-Schaetzung: Bei 50 Millionen Neuanmeldungen in den ersten 6 Monaten ca. 278.000 Verifizierungen pro Tag
  • Spitzenzeiten: Peak 3-5x des Durchschnitts, also 830.000-1.390.000 pro Tag
  • Anfragen pro Sekunde: Spitze ca. 16 TPS, mit Reserve fuer Burst-Traffic

UU PDP-Compliance-Implementierung

pub async fn cleanup_expired_records(
    pool: &PgPool,
) -> Result<u64, sqlx::Error> {
    let cutoff = Utc::now() - chrono::Duration::days(5 * 365);
    let result = sqlx::query("DELETE FROM audit_logs WHERE created_at < $1")
        .bind(cutoff).execute(pool).await?;
    Ok(result.rows_affected())
}

pub async fn handle_deletion_request(
    pool: &PgPool,
    nik_hash: &str,
) -> Result<DeletionReport, AppError> {
    let mut tx = pool.begin().await?;

    let templates_deleted = sqlx::query(
        "DELETE FROM biometric_templates WHERE nik_hash = $1"
    ).bind(nik_hash).execute(&mut *tx).await?.rows_affected();

    let logs_anonymized = sqlx::query(
        "UPDATE audit_logs SET nik_hash = 'anonymized' WHERE nik_hash = $1"
    ).bind(nik_hash).execute(&mut *tx).await?.rows_affected();

    tx.commit().await?;

    Ok(DeletionReport {
        templates_deleted,
        logs_anonymized,
        completed_at: Utc::now(),
    })
}

Bereitstellungsempfehlungen

Rechenzentren in Indonesien

Gemaess der KOMDIGI-Verordnung und UU PDP muss die biometrische Verarbeitung in indonesischen Rechenzentren erfolgen:

  • Primaer: Jakarta (Naehe zu den meisten Nutzern und der IKD-Plattform)
  • DR: Surabaya oder Bali (geografische Redundanz)
  • CDN: Landesweite Edge-Knoten (fuer SDK-Verteilung und statische Ressourcen)

Fazit

Der Aufbau eines biometrischen Verifizierungssystems, das der KOMDIGI-Verordnung entspricht, ist eine komplexe, aber handhabbare Engineering-Herausforderung. Wichtigste Erkenntnisse:

  1. Sicherheit zuerst: AES-256-Verschluesselung, TLS 1.3-Uebertragung, Zero-Trust-Architektur
  2. Compliance-getrieben: UU PDP-Datenschutz, 5 Jahre Audit-Log-Aufbewahrung, Nutzer-Loeschrecht
  3. Skalierbares Design: Horizontale Skalierung, mehrstufiges Caching, asynchrone Verarbeitung
  4. Lokalisierte Bereitstellung: Indonesische Rechenzentren, Optimierung fuer niedrige Bandbreite, Unterstuetzung der Geraetevielfalt
  5. Umfassendes Monitoring: Echtzeit-Metriken, Anomalieerkennung, Compliance-Berichte

Mit Rust und Axum bietet dieses System hervorragende Leistungs- und Sicherheitsgarantien bei Einhaltung der strengen regulatorischen Anforderungen Indonesiens.