Unzureichende oder unvollständige Datenentfernung innerhalb von Hardware-Komponenten
Beschreibung
Unzureichende oder unvollständige Datenentfernung innerhalb von Hardware-Komponenten tritt auf, wenn der Datenentfernungsprozess eines Produkts nicht alle sensiblen Informationen vollständig eliminiert, die in Hardware-Komponenten gespeichert sind. Physikalische Eigenschaften von Hardware-Geräten - wie magnetische Medienremanenz, Rest-ROM/RAM-Ladung oder Bildschirm-Einbrennen - können sensible Daten nach Löschung und Stromabschaltung zurückbehalten. Diese Schwäche stammt von Datenremanenz, einem Phänomen, bei dem wiederholte Schreibvorgänge an Speicherstellen physisch Zellen verändern, sodass selbst nach dem Löschen der Originaldaten diese Daten durch physikalische Charakterisierung dieser Zellen wiederhergestellt werden können.
Risiko
Unvollständige Datenentfernung hat schwerwiegende Sicherheitsauswirkungen. Kryptographische Schlüssel nach Löschung wiederherstellbar. Benutzerdaten verbleiben auf "gelöschten" Geräten. Werksreset löscht Geheimnisse nicht. Daten des Vorbesitzers zugänglich. Speicherremanenz ermöglicht Cold-Boot-Angriffe. Bildschirm-Einbrennen enthüllt sensible Muster. Physikalische Analyse stellt "gelöschte" Daten wieder her. Besonders schwerwiegend für Geräte, die den Besitzer wechseln.
Lösung
Wenden Sie Blinding- oder Masking-Techniken an, um Speicherverschleißmuster während der Architektur- und Designphase zu reduzieren. Modifizieren Sie Löschmethoden während der Implementierung - verwenden Sie mehrere Überschreibdurchgänge, fügen Sie Medienschutz hinzu oder zerstoren Sie Speichermedien physisch, um Datenwiederherstellung zu verhindern. Implementieren Sie ordnungsgemäße Secure-Erase-Sequenzen für verschiedene Speichertypen. Verifizieren Sie die Löscheffektivität durch Tests. Erwägen Sie manipulationssichere Gehäuse.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Scope: Vertraulichkeit Sensible Informationen einschließlich Schlüsseln und Benutzerdaten nach Löschung wiederherstellbar. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Unzureichende Datenlöschung
#include <stdint.h>
#include <string.h>
// VERWUNDBAR: Einfaches memset für sensible Daten
void vulnerable_erase_key(uint8_t* key, size_t len) {
// VERWUNDBAR: Einzeldurchgang-Überschreibung
memset(key, 0, len);
// Probleme:
// 1. Compiler kann "totes" memset wegoptimieren
// 2. Einzeldurchgang eliminiert Remanenz nicht
// 3. Speicherzellen behalten physische Spuren
}
// VERWUNDBAR: Werksreset löscht nicht alle Daten
typedef struct {
uint8_t wifi_password[64];
uint8_t user_pin[16];
uint8_t encryption_key[32];
uint32_t user_settings;
uint8_t firmware_config[256];
} device_storage_t;
void vulnerable_factory_reset(device_storage_t* storage) {
// VERWUNDBAR: Löscht nur einige Felder
memset(storage->user_pin, 0, sizeof(storage->user_pin));
memset(&storage->user_settings, 0, sizeof(storage->user_settings));
// VERWUNDBAR: Lässt WLAN-Passwort und Verschlüsselungsschlüssel!
// Anmeldedaten des Vorbesitzers bleiben zugänglich
// Auf Standardwerte zurücksetzen
storage->firmware_config[0] = 0x01; // Reset-Flag
}
// VERWUNDBAR: Speicher wird vor Ausschalten nicht gelöscht
void vulnerable_shutdown(void) {
// VERWUNDBAR: Schaltet nur aus ohne RAM zu löschen
// Sensible Daten verbleiben minutenlang in DRAM nach Ausschalten
// Cold-Boot-Angriff kann Verschlüsselungsschlüssel wiederherstellen
power_off();
}
// VERWUNDBAR: Dateilöschung löscht keine Daten
void vulnerable_delete_file(const char* filename) {
// VERWUNDBAR: Entfernt nur Verzeichniseintrag
// Tatsächliche Datenblöcke verbleiben auf Speicher
remove(filename);
// Dateiinhalt noch wiederherstellbar über:
// - Direkte Sektorzugriffe
// - Data-Carving-Tools
// - Physikalische Medienanalyse
}
// VERWUNDBAR: Hardware ohne ordnungsgemäßes Memory-Scrubbing
module vulnerable_memory_controller (
input wire clk,
input wire rst_n,
input wire erase_command,
input wire [15:0] erase_start,
input wire [15:0] erase_end,
output reg erase_done
);
reg [31:0] memory [0:65535];
reg [15:0] erase_addr;
reg erasing;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
erase_done <= 1'b0;
erasing <= 1'b0;
end else if (erase_command && !erasing) begin
erasing <= 1'b1;
erase_addr <= erase_start;
end else if (erasing) begin
// VERWUNDBAR: Einzeldurchgang mit Nullfüllung
memory[erase_addr] <= 32'h0000_0000;
if (erase_addr == erase_end) begin
erase_done <= 1'b1;
erasing <= 1'b0;
end else begin
erase_addr <= erase_addr + 1;
end
end
end
// VERWUNDBAR: Einzeldurchgang eliminiert Remanenz nicht
// Physikalische Analyse kann noch Originaldaten wiederherstellen
endmodule
// VERWUNDBAR: Schlüsselspeicher ohne Secure Erase
module vulnerable_key_storage (
input wire clk,
input wire rst_n,
input wire store_key,
input wire clear_key,
input wire [255:0] key_in,
output reg [255:0] key_out,
output reg key_valid
);
reg [255:0] stored_key;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
stored_key <= 256'b0; // VERWUNDBAR: Nur initiale Löschung
key_valid <= 1'b0;
end else if (store_key) begin
stored_key <= key_in;
key_valid <= 1'b1;
end else if (clear_key) begin
// VERWUNDBAR: Einzelnes Schreiben von Nullen
stored_key <= 256'b0;
key_valid <= 1'b0;
// Remanenz kann Schlüsselmuster behalten
end
end
assign key_out = stored_key;
endmodule
Sichere Lösung
// SICHER: Ordnungsgemäße sichere Datenlöschung
#include <stdint.h>
#include <string.h>
// SICHER: Sicheres Speicherlöschen, das Compiler nicht wegoptimiert
void secure_memzero(volatile void* ptr, size_t len) {
volatile uint8_t* p = (volatile uint8_t*)ptr;
// SICHER: volatile verhindert Optimierung
while (len--) {
*p++ = 0;
}
// SICHER: Memory-Barrier um sicherzustellen, dass Schreibvorgänge abgeschlossen werden
__asm__ __volatile__("" ::: "memory");
}
// SICHER: Mehrdurchgang-Überschreibung für sensible Daten
void secure_erase_key(uint8_t* key, size_t len) {
// SICHER: Mehrere Überschreibmuster
// Muster 1: Alle Nullen
for (size_t i = 0; i < len; i++) {
((volatile uint8_t*)key)[i] = 0x00;
}
// Muster 2: Alle Einsen
for (size_t i = 0; i < len; i++) {
((volatile uint8_t*)key)[i] = 0xFF;
}
// Muster 3: Zufallsdaten
uint8_t random_bytes[256];
get_random_bytes(random_bytes, len > 256 ? 256 : len);
for (size_t i = 0; i < len; i++) {
((volatile uint8_t*)key)[i] = random_bytes[i % 256];
}
// Muster 4: Finale Nullen
for (size_t i = 0; i < len; i++) {
((volatile uint8_t*)key)[i] = 0x00;
}
// SICHER: Zufallspuffer ebenfalls löschen
secure_memzero(random_bytes, sizeof(random_bytes));
}
// SICHER: Vollständiger Werksreset
typedef struct {
uint8_t wifi_password[64];
uint8_t user_pin[16];
uint8_t encryption_key[32];
uint32_t user_settings;
uint8_t firmware_config[256];
} device_storage_t;
void secure_factory_reset(device_storage_t* storage) {
// SICHER: ALLE sensiblen Felder löschen
// WLAN-Passwort - Secure Erase
secure_erase_key(storage->wifi_password, sizeof(storage->wifi_password));
// PIN - Secure Erase
secure_erase_key(storage->user_pin, sizeof(storage->user_pin));
// Verschlüsselungsschlüssel - am kritischsten
secure_erase_key(storage->encryption_key, sizeof(storage->encryption_key));
// Benutzereinstellungen
storage->user_settings = 0;
// SICHER: Gesamte Struktur mit mehreren Durchgängen löschen
secure_memzero(storage, sizeof(device_storage_t));
// SICHER: Bekannte gute Standardwerte schreiben
init_default_config(storage);
// SICHER: Löschung verifizieren
if (!verify_erasure_complete(storage)) {
// Löschung fehlgeschlagen - sicheren Fehlermodus betreten
enter_secure_lockdown();
}
}
// SICHER: RAM vor Herunterfahren löschen
void secure_shutdown(void) {
// SICHER: Alle sensiblen Speicherbereiche löschen
clear_key_storage();
clear_session_data();
clear_crypto_context();
// SICHER: Gesamten RAM scrubben wenn möglich
scrub_ram_region(RAM_START, RAM_SIZE);
// SICHER: Sicherstellen, dass Scrubbing vor Ausschalten abgeschlossen ist
sync_memory();
power_off();
}
// SICHER: Sichere Dateilöschung
void secure_delete_file(const char* filename) {
FILE* f = fopen(filename, "r+b");
if (f == NULL) return;
// SICHER: Dateigröße ermitteln
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
// SICHER: Dateiinhalt mit mehreren Mustern überschreiben
uint8_t buffer[4096];
// Durchgang 1: Nullen
memset(buffer, 0x00, sizeof(buffer));
for (long i = 0; i < size; i += sizeof(buffer)) {
size_t to_write = (size - i < sizeof(buffer)) ? (size - i) : sizeof(buffer);
fwrite(buffer, 1, to_write, f);
}
fflush(f);
fsync(fileno(f));
// Durchgang 2: Einsen
memset(buffer, 0xFF, sizeof(buffer));
fseek(f, 0, SEEK_SET);
for (long i = 0; i < size; i += sizeof(buffer)) {
size_t to_write = (size - i < sizeof(buffer)) ? (size - i) : sizeof(buffer);
fwrite(buffer, 1, to_write, f);
}
fflush(f);
fsync(fileno(f));
// Durchgang 3: Zufallsdaten
fseek(f, 0, SEEK_SET);
for (long i = 0; i < size; i += sizeof(buffer)) {
get_random_bytes(buffer, sizeof(buffer));
size_t to_write = (size - i < sizeof(buffer)) ? (size - i) : sizeof(buffer);
fwrite(buffer, 1, to_write, f);
}
fflush(f);
fsync(fileno(f));
fclose(f);
// SICHER: Jetzt Verzeichniseintrag entfernen
remove(filename);
}
// SICHER: Hardware mit ordnungsgemäßem Memory-Scrubbing
module secure_memory_controller (
input wire clk,
input wire rst_n,
input wire erase_command,
input wire [15:0] erase_start,
input wire [15:0] erase_end,
input wire [1:0] erase_level, // 0=schnell, 1=standard, 2=sicher
output reg erase_done,
output reg erase_verified
);
reg [31:0] memory [0:65535];
reg [15:0] erase_addr;
reg [2:0] pass_counter;
reg [1:0] current_level;
reg erasing;
reg verifying;
// SICHER: LFSR für pseudo-zufälliges Muster
reg [31:0] lfsr;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
erase_done <= 1'b0;
erase_verified <= 1'b0;
erasing <= 1'b0;
verifying <= 1'b0;
pass_counter <= 3'b0;
lfsr <= 32'hDEADBEEF;
end else if (erase_command && !erasing && !verifying) begin
erasing <= 1'b1;
erase_addr <= erase_start;
pass_counter <= 3'b0;
current_level <= erase_level;
erase_done <= 1'b0;
erase_verified <= 1'b0;
end else if (erasing) begin
// SICHER: Mehrdurchgang-Löschung basierend auf Sicherheitsstufe
case (pass_counter)
3'd0: begin
// Durchgang 1: Alle Nullen
memory[erase_addr] <= 32'h0000_0000;
end
3'd1: begin
// Durchgang 2: Alle Einsen
memory[erase_addr] <= 32'hFFFF_FFFF;
end
3'd2: begin
// Durchgang 3: Alternierendes Muster
memory[erase_addr] <= 32'hAAAA_AAAA;
end
3'd3: begin
// Durchgang 4: Inverses Alternieren
memory[erase_addr] <= 32'h5555_5555;
end
3'd4: begin
// Durchgang 5: Zufällig (nur sichere Stufe)
lfsr <= {lfsr[30:0], lfsr[31] ^ lfsr[21] ^ lfsr[1] ^ lfsr[0]};
memory[erase_addr] <= lfsr;
end
3'd5: begin
// Durchgang 6: Finale Nullen
memory[erase_addr] <= 32'h0000_0000;
end
endcase
if (erase_addr == erase_end) begin
// Ein Durchgang abgeschlossen
if ((current_level == 2'b00 && pass_counter >= 3'd1) ||
(current_level == 2'b01 && pass_counter >= 3'd3) ||
(current_level == 2'b10 && pass_counter >= 3'd5)) begin
// SICHER: Verifizierungsphase betreten
erasing <= 1'b0;
verifying <= 1'b1;
erase_addr <= erase_start;
end else begin
pass_counter <= pass_counter + 1;
erase_addr <= erase_start;
end
end else begin
erase_addr <= erase_addr + 1;
end
end else if (verifying) begin
// SICHER: Verifizieren, dass alle Stellen Null sind
if (memory[erase_addr] != 32'h0000_0000) begin
// Verifizierung fehlgeschlagen - Löschung neu starten
verifying <= 1'b0;
erasing <= 1'b1;
erase_addr <= erase_start;
pass_counter <= 3'b0;
end else if (erase_addr == erase_end) begin
// SICHER: Verifizierung abgeschlossen
erase_done <= 1'b1;
erase_verified <= 1'b1;
verifying <= 1'b0;
end else begin
erase_addr <= erase_addr + 1;
end
end
end
endmodule
// SICHER: Schlüsselspeicher mit Secure Erase
module secure_key_storage (
input wire clk,
input wire rst_n,
input wire store_key,
input wire clear_key,
input wire [255:0] key_in,
output reg [255:0] key_out,
output reg key_valid,
output reg erase_complete
);
reg [255:0] stored_key;
reg [2:0] erase_pass;
reg erasing;
// SICHER: Mehrdurchgang-Schlüssellöschung
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
stored_key <= 256'b0;
key_valid <= 1'b0;
erase_complete <= 1'b0;
erasing <= 1'b0;
erase_pass <= 3'b0;
end else if (store_key && !erasing) begin
stored_key <= key_in;
key_valid <= 1'b1;
erase_complete <= 1'b0;
end else if (clear_key && !erasing) begin
erasing <= 1'b1;
erase_pass <= 3'b0;
key_valid <= 1'b0;
erase_complete <= 1'b0;
end else if (erasing) begin
case (erase_pass)
3'd0: stored_key <= 256'hFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF;
3'd1: stored_key <= 256'h0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000_0000;
3'd2: stored_key <= 256'hAAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA_AAAA;
3'd3: stored_key <= 256'h5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555_5555;
3'd4: stored_key <= 256'b0; // Finale Nullen
3'd5: begin
erase_complete <= 1'b1;
erasing <= 1'b0;
end
endcase
if (erase_pass < 3'd5) begin
erase_pass <= erase_pass + 1;
end
end
end
assign key_out = key_valid ? stored_key : 256'b0;
endmodule
CVE-Beispiele
- CVE-2019-8575: Werksreset löschte nicht alle Benutzerdaten, was Wiederherstellung von WLAN-Anmeldedaten ermöglichte.
- CVE-2020-8911: SSD-Firmware implementierte Secure Erase nicht ordnungsgemäß, wodurch verschlüsselte Daten wiederherstellbar blieben.
Verwandte CWEs
- CWE-226: Sensitive Information in Resource Not Removed Before Reuse (übergeordnet)
- CWE-1330: Remanent Data Readable after Memory Erase (untergeordnet)
- CWE-1208: Cross-Cutting Problems (Kategorie)
- CWE-212: Improper Removal of Sensitive Information Before Storage or Transfer (verwandt)
Referenzen
- MITRE Corporation. "CWE-1301: Insufficient or Incomplete Data Removal within Hardware Component." https://cwe.mitre.org/data/definitions/1301.html
- NIST SP 800-88: Guidelines for Media Sanitization
- IEEE. "Data Remanence in Semiconductor Devices"