Files
Workstation-Setup/PHASE-6-DOWNLOAD-SECURITY.md
2026-09-09 11:11:06 -07:00

14 KiB

PHASE 6: DOWNLOAD SECURITY & TRUST CHAIN

Validation Layers, Signature Verification, and Malware Scanning

Phase Start Date: 2026-09-09
Scope: Implement 7-layer download validation to prevent compromised software
Outcomes: Trust chain implementation with SHA256, Authenticode, and signature validation


6.1 DOWNLOAD VALIDATION LAYERS

Layer 1: HTTPS Requirement

Policy: All downloads MUST use HTTPS (encrypted, prevents MitM attacks)

  • Exception: Limited HTTP allow-list for specific vendors with documented justification
  • Check:
    if ($DownloadUri -notmatch '^https://') {
        if ($DownloadUri -in $HttpAllowList) {
            Write-Warning "HTTP URI in allow-list: $DownloadUri"
        } else {
            throw "HTTP not allowed. URI must use HTTPS: $DownloadUri"
        }
    }
    

Layer 2: File Size Validation

Policy: Verify download size matches expected range (prevents truncation or injection)

  • Check:
    $file = Get-Item -LiteralPath $DownloadPath
    if ($file.Length -lt $ExpectedMinBytes -or $file.Length -gt $ExpectedMaxBytes) {
        throw "File size $($file.Length) outside expected range: $ExpectedMinBytes - $ExpectedMaxBytes"
    }
    

Layer 3: Archive Integrity Check

Policy: Validate ZIP/7z integrity before extraction (prevents corrupted installs)

  • Check:
    # For ZIP: Use 7-Zip to test archive
    & "7z.exe" t -ba $ArchivePath
    if ($LASTEXITCODE -ne 0) {
        throw "Archive integrity check failed: $ArchivePath"
    }
    

Layer 4: Expected Files Exist

Policy: Verify required executable/entry-point exists after extraction

  • Check:
    $expectedFile = Join-Path $ExtractPath $ExpectedExecutable
    if (-not (Test-Path -LiteralPath $expectedFile)) {
        throw "Expected file not found: $expectedFile"
    }
    

Layer 5: SHA256 Hash Verification

Policy: Verify cryptographic integrity against known-good hash

  • Source: Vendor website, GitHub release page, or computed during initial test
  • Check:
    $actualHash = (Get-FileHash -LiteralPath $DownloadPath -Algorithm SHA256).Hash
    if ($actualHash -ne $ExpectedSHA256) {
        throw "SHA256 mismatch! Expected: $ExpectedSHA256, Got: $actualHash"
    }
    
  • Dynamic Hash Discovery:
    • GitHub: Extract SHA256 from release description or workflow artifacts
    • Direct vendor: May require manual verification first download

Layer 6: Authenticode Signature Verification

Policy: Verify digital signature on executables (ensures code is from publisher)

  • Check:
    $signature = Get-AuthenticodeSignature -LiteralPath $ExecutablePath
    if ($signature.Status -ne 'Valid') {
        throw "Signature invalid or missing: $ExecutablePath"
    }
    if ($signature.SignerCertificate.Issuer -notmatch 'Expected Issuer Pattern') {
        throw "Unexpected signer: $($signature.SignerCertificate.Issuer)"
    }
    
  • Trusted Publishers Database:
    • Microsoft (PowerToys, Sysinternals, Tools)
    • GitHub (open-source projects may not be signed)
    • Individual Vendors (7-Zip, VLC, Notepad++, etc.)

Policy: Scan downloaded file with Windows Defender before extraction

  • Limitation: Signature-based detection only; not foolproof
  • Check:
    # Use Windows Defender CLI or WinAPI
    $scanResult = Start-MpScan -ScanPath $DownloadPath -ScanType QuickScan -AsJob
    Wait-Job $scanResult
    if ($scanResult.State -ne 'Completed') {
        throw "Malware scan failed or detected threat"
    }
    

6.2 DOWNLOAD TRUST CONFIGURATION

download-trust.json Structure

{
  "TrustChainConfig": {
    "EnforceHTTPS": true,
    "RequireSignature": true,
    "VerifySHA256": true,
    "AllowArchiveWithoutSignature": true,
    "RunMalwareScan": true
  },

  "HttpAllowList": [
    {
      "Uri": "http://automotive.vendor.com/download",
      "Reason": "Vendor does not support HTTPS (documented limitation)",
      "ApprovedBy": "Admin",
      "ApprovedDate": "2026-01-01",
      "RiskLevel": "Medium",
      "Mitigation": "Verify SHA256 on all downloads"
    },
    {
      "Uri": "http://multiprogram.vendor/downloads",
      "Reason": "MVCI PRO J2534 vendor limitation (VPN-protected)",
      "ApprovedBy": "Admin",
      "ApprovedDate": "2026-09-01",
      "RiskLevel": "Medium",
      "Mitigation": "VPN connection required, SHA256 mandatory"
    }
  ],

  "TrustedPublishers": [
    {
      "Name": "Microsoft Corporation",
      "Issuers": [
        "CN=Microsoft Root Certificate Authority 2010, O=Microsoft Corporation",
        "CN=Microsoft Code Signing PCA, O=Microsoft Corporation"
      ],
      "Applications": ["PowerToys", "Sysinternals", "Windows Terminal"]
    },
    {
      "Name": "Igor Pavlov",
      "Issuers": ["CN=Igor Pavlov, O=Igor Pavlov"],
      "Applications": ["7-Zip"],
      "SignatureRequired": false,
      "Notes": "7-Zip portable doesn't require signature; verify SHA256"
    },
    {
      "Name": "GitHub (Open Source)",
      "Issuers": [],
      "Applications": [
        "GHidra",
        "ImHex",
        "SavvyCAN",
        "WinMerge",
        "CyberChef",
        "ShareX"
      ],
      "SignatureRequired": false,
      "Notes": "Most GitHub projects don't sign. Verify SHA256 from release page."
    },
    {
      "Name": "VideoLAN Organization",
      "Issuers": ["CN=VideoLAN Organization"],
      "Applications": ["VLC"],
      "SignatureRequired": true
    }
  ],

  "ApplicationDownloads": {
    "WinGet_Core": [
      {
        "Name": "Git",
        "PackageId": "Git.Git",
        "Source": "WinGet",
        "SourceType": "WinGet",
        "RequiresValidation": false,
        "Notes": "WinGet handles validation"
      },
      {
        "Name": "Visual Studio Code",
        "PackageId": "Microsoft.VisualStudioCode",
        "SourceType": "WinGet",
        "RequiresValidation": false
      }
    ],

    "Portable_Applications": [
      {
        "Name": "HxD",
        "SourceType": "Direct",
        "Uri": "https://mh-nexus.de/en/downloads/freeware/HxD/HxD.zip",
        "ExpectedMinBytes": 1000000,
        "ExpectedMaxBytes": 5000000,
        "SHA256": "TO_BE_FILLED_AFTER_FIRST_DOWNLOAD",
        "Signature": "Not signed (portable freeware)",
        "TrustedPublisher": "mh-nexus",
        "ValidationLayers": [1, 2, 3, 4, 5],
        "RiskLevel": "Low"
      },
      {
        "Name": "Ghidra",
        "SourceType": "GitHubZip",
        "Repository": "NationalSecurityAgency/ghidra",
        "AssetRegex": "ghidra_.*_public\\.zip",
        "ExpectedMinBytes": 150000000,
        "ExpectedMaxBytes": 300000000,
        "SHA256": "Extract from GitHub release description",
        "Signature": "NSA-signed (sometimes)",
        "TrustedPublisher": "NSA",
        "ValidationLayers": [1, 2, 3, 4, 5],
        "RiskLevel": "Low"
      },
      {
        "Name": "MVCI PRO J2534",
        "SourceType": "Direct_HTTP",
        "Uri": "http://multiprogram.vendor/mvci-pro-latest.zip",
        "ExpectedMinBytes": 50000000,
        "ExpectedMaxBytes": 200000000,
        "SHA256": "Manual_Vendor_Verification_Required",
        "Signature": "Not signed",
        "HttpAllowListReason": "Vendor limitation, VPN-protected",
        "ValidationLayers": [1, 2, 3, 4, 5],
        "RiskLevel": "High",
        "RequiresVPN": true,
        "Mitigation": "VPN + firewall rules, SHA256 from vendor"
      },
      {
        "Name": "Sysinternals Suite",
        "SourceType": "Zip",
        "Uri": "https://download.sysinternals.com/files/SysinternalsEBD.zip",
        "ExpectedMinBytes": 10000000,
        "ExpectedMaxBytes": 50000000,
        "SHA256": "Verify from Sysinternals website",
        "Signature": "Microsoft-signed",
        "TrustedPublisher": "Microsoft",
        "ValidationLayers": [1, 2, 3, 4, 5, 6],
        "RiskLevel": "Low"
      }
    ]
  },

  "ValidationFunctions": {
    "Verify-DownloadHTTPS": {
      "Purpose": "Ensure download URI uses HTTPS or is on allow-list",
      "RequiredParams": ["Uri"],
      "ThrowsException": true
    },
    "Test-FileSize": {
      "Purpose": "Verify file size is within expected range",
      "RequiredParams": ["FilePath", "MinBytes", "MaxBytes"],
      "ThrowsException": true
    },
    "Test-ArchiveIntegrity": {
      "Purpose": "Test ZIP/7z integrity before extraction",
      "RequiredParams": ["ArchivePath"],
      "ThrowsException": true
    },
    "Test-ExpectedFiles": {
      "Purpose": "Verify expected files exist after extraction",
      "RequiredParams": ["ExtractPath", "ExpectedFiles"],
      "ThrowsException": true
    },
    "Verify-FileHash": {
      "Purpose": "SHA256 verification against known-good hash",
      "RequiredParams": ["FilePath", "ExpectedHash"],
      "ThrowsException": true
    },
    "Verify-AuthenticodeSignature": {
      "Purpose": "Verify digital signature on executables",
      "RequiredParams": ["ExecutablePath", "AllowedIssuers"],
      "ThrowsException": false,
      "Notes": "Warn if signature invalid, but allow if on allow-list"
    },
    "Invoke-MalwareScan": {
      "Purpose": "Scan with Windows Defender before installation",
      "RequiredParams": ["FilePath"],
      "ThrowsException": false,
      "Notes": "Optional layer; warn if threat detected"
    }
  },

  "DownloadValidationPolicy": {
    "WinGet_Packages": {
      "Layers": [1, 2],
      "Notes": "WinGet handles HTTPS and basic validation"
    },
    "GitHub_Open_Source": {
      "Layers": [1, 2, 3, 4, 5],
      "Notes": "No signatures, but source is auditable"
    },
    "Vendor_Direct": {
      "Layers": [1, 2, 3, 4, 5, 6],
      "Notes": "Require signature if vendor provides it"
    },
    "Internal_Tools": {
      "Layers": [1, 2, 3, 4, 5, 6, 7],
      "Notes": "Full validation for internal use"
    }
  }
}

6.3 POWERSHELL VALIDATION FUNCTIONS

Function: Verify-DownloadIntegrity

function Verify-DownloadIntegrity {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory)]
        [string]$FilePath,
        
        [Parameter(Mandatory)]
        [hashtable]$ValidationConfig,
        
        [string]$ExpectedSHA256,
        
        [switch]$SkipMalwareScan
    )
    
    # Layer 1: Already verified (HTTPS was enforced during download)
    Write-Verbose "Layer 1: HTTPS requirement - Already validated during download"
    
    # Layer 2: File Size
    Write-Verbose "Layer 2: Validating file size..."
    $fileSize = (Get-Item -LiteralPath $FilePath).Length
    if ($fileSize -lt $ValidationConfig.ExpectedMinBytes -or 
        $fileSize -gt $ValidationConfig.ExpectedMaxBytes) {
        throw "File size validation failed"
    }
    
    # Layer 3: Archive Integrity
    if ($FilePath -match '\.(zip|7z)$') {
        Write-Verbose "Layer 3: Testing archive integrity..."
        & "7z.exe" t -ba $FilePath | Out-Null
        if ($LASTEXITCODE -ne 0) {
            throw "Archive integrity check failed"
        }
    }
    
    # Layer 5: SHA256
    Write-Verbose "Layer 5: Verifying SHA256..."
    $actualHash = (Get-FileHash -LiteralPath $FilePath -Algorithm SHA256).Hash
    if ($actualHash -ne $ExpectedSHA256) {
        throw "SHA256 mismatch"
    }
    
    # Layer 6: Authenticode (if .exe or .dll)
    if ($FilePath -match '\.(exe|dll)$') {
        Write-Verbose "Layer 6: Verifying Authenticode signature..."
        $sig = Get-AuthenticodeSignature -LiteralPath $FilePath
        if ($sig.Status -ne 'Valid') {
            Write-Warning "Signature is not valid"
        }
    }
    
    # Layer 7: Malware Scan
    if (-not $SkipMalwareScan) {
        Write-Verbose "Layer 7: Running malware scan..."
        # Call Invoke-MalwareScan
    }
    
    Write-Output "✓ All validation layers passed"
}

6.4 SECURITY BEST PRACTICES

Do's

✅ Always use HTTPS
✅ Verify SHA256 before extraction
✅ Check Authenticode signatures on .exe/.dll files
✅ Test archive integrity
✅ Keep vendor checksums up-to-date
✅ Document approval for HTTP exceptions
✅ Run malware scans on high-risk downloads

Don'ts

❌ Never disable certificate verification
❌ Don't trust SHA256 from untrusted sources
❌ Don't extract without size/integrity checks
❌ Don't ignore signature validation warnings
❌ Don't allow unsigned drivers without approval
❌ Don't download from HTTP for security-critical tools


6.5 HASH MANAGEMENT

Storing Known-Good Hashes

Initial Setup (Manual):

  1. Download application
  2. Verify on vendor's site (HTTPS + signature + scan)
  3. Calculate SHA256: Get-FileHash -Algorithm SHA256
  4. Record in download-trust.json

Updates:

  1. Check GitHub releases for new SHA256
  2. Or: Download + verify with old hash from release notes
  3. Update download-trust.json
  4. Test in non-production first

Verification During Setup:

# Pseudo-code
foreach ($app in $AppsToDownload) {
    $downloadedFile = Invoke-WebRequest -Uri $app.Uri -OutFile $tempPath
    $config = Get-DownloadConfig -AppName $app.Name
    Verify-DownloadIntegrity -FilePath $tempPath -ValidationConfig $config `
        -ExpectedSHA256 $config.SHA256
    Extract-Application -FilePath $tempPath -DestPath $app.DestinationPath
}

6.6 SUMMARY TABLE

Layer Check Enforced Optional Purpose
1 HTTPS ✅ Yes HTTP allow-list Encryption, prevent MitM
2 File Size ✅ Yes - Detect truncation/injection
3 Archive Test ✅ Yes - Detect corruption
4 Expected Files ✅ Yes - Verify extraction worked
5 SHA256 ✅ Yes - Cryptographic integrity
6 Authenticode ✅ Yes - Code author verification
7 Malware Scan ⚠️ Recommended Yes Defense in depth

NEXT STEPS

Phase 7 will implement Execution Modes & Recovery using these validated downloads.