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.)
Layer 7: Malware Scanning (Optional but Recommended)
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):
- Download application
- Verify on vendor's site (HTTPS + signature + scan)
- Calculate SHA256:
Get-FileHash -Algorithm SHA256 - Record in
download-trust.json
Updates:
- Check GitHub releases for new SHA256
- Or: Download + verify with old hash from release notes
- Update
download-trust.json - 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.