446 lines
14 KiB
Markdown
446 lines
14 KiB
Markdown
# 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:**
|
|
```powershell
|
|
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:**
|
|
```powershell
|
|
$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:**
|
|
```powershell
|
|
# 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:**
|
|
```powershell
|
|
$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:**
|
|
```powershell
|
|
$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:**
|
|
```powershell
|
|
$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:**
|
|
```powershell
|
|
# 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
|
|
|
|
```json
|
|
{
|
|
"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
|
|
|
|
```powershell
|
|
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:**
|
|
```powershell
|
|
# 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.
|
|
|