Private/Utilities/Test-BackupFileIntegrity.ps1
|
function Test-BackupFileIntegrity { <# .SYNOPSIS Validates the integrity of backup files with comprehensive security and data validation .DESCRIPTION Performs comprehensive validation of backup files including file existence, SHA256 hash verification, SDDL format validation, metadata structure validation, and security checks. Designed for enterprise-grade backup integrity validation with detailed reporting and correlation tracking. .PARAMETER BackupFilePath The absolute or relative path to the backup file to validate. Supports Windows and UNC path formats. .PARAMETER CorrelationId Optional correlation ID for tracking validation operations. Auto-generated if not provided. .OUTPUTS Returns a validation result object with the following properties: - IsValid: Boolean indicating overall validation success - ErrorMessage: Primary error message if validation fails - ValidationDetails: Array of detailed validation issues - BackupData: The validated backup data object (null if invalid) .EXAMPLE Test-BackupFileIntegrity -BackupFilePath "C:\Backups\backup_20240702.xml" Validates the specified backup file and returns validation results. .NOTES Author: Jeffrey Stuhr Version: 2.1.0 SECURITY CONSIDERATIONS: - Validates file paths to prevent traversal attacks - Sanitizes error messages to prevent information disclosure - Implements secure hash validation - Validates SDDL format to prevent malicious content TROUBLESHOOTING: - File not found: Check path and permissions - Hash validation: Backup may be corrupted - SDDL validation: Backup contains invalid security descriptor #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BackupFilePath, [Parameter()] [string]$CorrelationId = [System.Guid]::NewGuid().ToString() ) begin { Write-StructuredLog -Level 'Information' -Message "Starting backup integrity validation" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath } } process { # Initialize result object $result = [PSCustomObject]@{ IsValid = $false ErrorMessage = $null ValidationDetails = @() BackupData = $null } try { # Step 1: File existence validation if (-not (Test-Path -Path $BackupFilePath -PathType Leaf)) { $result.ErrorMessage = "Backup file not found: $BackupFilePath" $result.ValidationDetails += "File does not exist" Write-StructuredLog -Level 'Warning' -Message "Backup file not found" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath } return $result } # Step 2: Load backup data try { $backupData = Import-Clixml -Path $BackupFilePath -ErrorAction Stop } catch { $result.ErrorMessage = "Validation error: $($_.Exception.Message)" $result.ValidationDetails += "Failed to load backup file" Write-StructuredLog -Level 'Error' -Message "Error during backup integrity validation" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath Error = $_.Exception.Message } return $result } # Step 3: Validate backup data structure if ($null -eq $backupData) { $result.ErrorMessage = "Backup file structure validation failed" $result.ValidationDetails += "Backup data is null" return $result } # Check required properties $requiredProperties = @('ObjectDN', 'BackupDate', 'SDDL', 'SDDLHash', 'CorrelationId') foreach ($property in $requiredProperties) { if (-not $backupData.PSObject.Properties[$property]) { $result.ValidationDetails += "Missing required property: $property" } } # Step 4: Validate backup version compatibility if ($backupData.PSObject.Properties['BackupVersion']) { $version = $backupData.BackupVersion if ($version -match '^(\d+)\.') { $majorVersion = [int]$Matches[1] if ($majorVersion -gt 2) { $result.ValidationDetails += "Unsupported backup version: $version" } } } # Step 5: SHA256 hash validation if ($backupData.SDDL -and $backupData.SDDLHash) { try { $sddlBytes = [System.Text.Encoding]::UTF8.GetBytes($backupData.SDDL) $sha256 = [System.Security.Cryptography.SHA256]::Create() try { $computedHashBytes = $sha256.ComputeHash($sddlBytes) $computedHash = [System.Convert]::ToBase64String($computedHashBytes) if ($computedHash -ne $backupData.SDDLHash) { $result.ErrorMessage = "SHA256 hash verification failed" $result.ValidationDetails += "Hash mismatch detected" Write-StructuredLog -Level 'Error' -Message "SHA256 hash verification failed" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath ExpectedHash = $backupData.SDDLHash ComputedHash = $computedHash } return $result } } finally { $sha256.Dispose() } } catch { $result.ErrorMessage = "Validation error: $($_.Exception.Message)" $result.ValidationDetails += "Hash computation failed" return $result } } # Step 6: SDDL format validation if ($backupData.PSObject.Properties['SDDL']) { try { # Check for empty or whitespace-only SDDL if ([string]::IsNullOrWhiteSpace($backupData.SDDL)) { $result.ValidationDetails += "Invalid SDDL format" } elseif ($backupData.SDDL.Length -lt 5) { # SDDL must be at least 5 characters (minimum valid format like "D:(A)") $result.ValidationDetails += "Invalid SDDL format" } else { # Basic SDDL format validation - check for DACL presence # Valid SDDL should have a DACL portion starting with "D:" if ($backupData.SDDL -notmatch 'D:\([^)]*\)') { $result.ValidationDetails += "Invalid SDDL format" } } } catch { $result.ValidationDetails += "Invalid SDDL format" } } else { # Missing SDDL property entirely $result.ValidationDetails += "Missing SDDL property" } # Step 7: Structure validation summary if ($result.ValidationDetails.Count -gt 0) { $result.ErrorMessage = "Backup file structure validation failed" Write-StructuredLog -Level 'Warning' -Message "Backup structure validation failed" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath ValidationIssues = $result.ValidationDetails.Count } return $result } # Step 8: Success case $result.IsValid = $true $result.BackupData = $backupData Write-StructuredLog -Level 'Information' -Message "Backup integrity validation completed successfully" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath ObjectDN = $backupData.ObjectDN } } catch { $result.ErrorMessage = "Validation error: $($_.Exception.Message)" $result.ValidationDetails += "Unexpected error during validation" Write-StructuredLog -Level 'Error' -Message "Error during backup integrity validation" -Component 'BackupValidation' -CorrelationId $CorrelationId -Data @{ BackupFilePath = $BackupFilePath Error = $_.Exception.Message } } return $result } } |