为什么需要把安全响应头导出成 CSV 报告
用 PowerShell 检测网站安全头,很多人只停留在「能看到结果」这一步。但真正做运维、做等保测评、做周报月报,你得把结果变成一份可存档、可对比、可发送给上级的 CSV 报告。
这篇文章讲清楚:怎么用 PowerShell 一键扫描多个域名的安全响应头,然后把结果导出成标准化 CSV 报告,包含评分、每项头的状态、建议修复措施。
一、先搞定:Invoke-WebRequest 获取安全响应头
PowerShell 原生没有 curl,但 Invoke-WebRequest 可以拿到响应头。核心代码:
$urls = @(\"https://www.youres.cn\",\"https://www.baidu.com\")
$results = @()
foreach ($url in $urls) {
try {
$resp = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10
$headers = $resp.Headers
$results += [PSCustomObject]@{
URL = $url
StrictTransportSecurity = $headers['Strict-Transport-Security'] -join ','
XContentTypeOptions = $headers['X-Content-Type-Options'] -join ','
XFrameOptions = $headers['X-Frame-Options'] -join ','
XXSSProtection = $headers['X-XSS-Protection'] -join ','
ContentSecurityPolicy = $headers['Content-Security-Policy'] -join ','
ReferrerPolicy = $headers['Referrer-Policy'] -join ','
PermissionsPolicy = $headers['Permissions-Policy'] -join ','
StatusCode = $resp.StatusCode
CheckTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
}
} catch {
$results += [PSCustomObject]@{
URL = $url
StrictTransportSecurity = 'ERROR'
XContentTypeOptions = 'ERROR'
XFrameOptions = 'ERROR'
XXSSProtection = 'ERROR'
ContentSecurityPolicy = 'ERROR'
ReferrerPolicy = 'ERROR'
PermissionsPolicy = 'ERROR'
StatusCode = 0
CheckTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
}
}
}
$results | Format-Table
说明:-UseBasicParsing 避免 IE DOM 依赖(Server Core 环境必须加);-TimeoutSec 10 防止挂死;错误用 try/catch 兜底。
二、给每个网站打安全评分(核心实战)
光有头不够,还得有量化评分。按 Security Headers 官网标准,我写一个评分函数:
function Get-SecurityHeaderScore {
param($headersObj)
$score = 0
$max = 0
# HSTS(必须 HTTPS)
$max += 20
if ($headersObj.StrictTransportSecurity -and $headersObj.StrictTransportSecurity -ne 'ERROR') { $score += 20 }
# X-Content-Type-Options
$max += 10
if ($headersObj.XContentTypeOptions -eq 'nosniff') { $score += 10 }
# X-Frame-Options
$max += 10
if ($headersObj.XFrameOptions -in 'DENY','SAMEORIGIN') { $score += 10 }
# X-XSS-Protection
$max += 10
if ($headersObj.XXSSProtection -eq '1; mode=block') { $score += 10 }
# Content-Security-Policy
$max += 30
if ($headersObj.ContentSecurityPolicy -and $headersObj.ContentSecurityPolicy -ne 'ERROR') { $score += 30 }
# Referrer-Policy
$max += 10
if ($headersObj.ReferrerPolicy -and $headersObj.ReferrerPolicy -ne 'ERROR') { $score += 10 }
# Permissions-Policy
$max += 10
if ($headersObj.PermissionsPolicy -and $headersObj.PermissionsPolicy -ne 'ERROR') { $score += 10 }
return [PSCustomObject]@{
Score = $score
MaxScore = $max
Grade = if ($score -ge 80) { 'A' } elseif ($score -ge 60) { 'B' } elseif ($score -ge 40) { 'C' } elseif ($score -ge 20) { 'D' } else { 'F' }
}
}
三、导出 CSV 报告(含中文列名)
PowerShell 导出 CSV 最大的坑是中文乱码。必须用 Export-Csv -Encoding UTF8:
$report = @()
foreach ($r in $results) {
$gradeObj = Get-SecurityHeaderScore -headersObj $r
$report += [PSCustomObject]@{
检测网址 = $r.URL
HSTS = if($r.StrictTransportSecurity -and $r.StrictTransportSecurity -ne 'ERROR') { '✅ 已配置' } else { '❌ 缺失' }
XContentTypeOptions = if($r.XContentTypeOptions -eq 'nosniff') { '✅ nosniff' } else { '❌ 缺失或错误' }
XFrameOptions = if($r.XFrameOptions -in 'DENY','SAMEORIGIN') { '✅ ' + $r.XFrameOptions } else { '❌ 缺失' }
XXSSProtection = if($r.XXSSProtection -eq '1; mode=block') { '✅ 已配置' } else { '❌ 缺失' }
ContentSecurityPolicy = if($r.ContentSecurityPolicy -and $r.ContentSecurityPolicy -ne 'ERROR') { '✅ 已配置' } else { '❌ 缺失' }
ReferrerPolicy = if($r.ReferrerPolicy -and $r.ReferrerPolicy -ne 'ERROR') { '✅ ' + $r.ReferrerPolicy } else { '❌ 缺失' }
安全评分 = $gradeObj.Score
评级 = $gradeObj.Grade
检测时间 = $r.CheckTime
}
}
$report | Export-Csv -Path \".\\SecurityHeaderReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv\" -Encoding UTF8 -NoTypeInformation
Write-Host \"报告已导出,共$($report.Count) 条记录\"
关键点:-Encoding UTF8 保证 Excel 打开不乱码;-NoTypeInformation 去掉文件头部的类型信息,更干净。
四、完整一键脚本(复制即用)
把上面所有代码拼起来,就是一个完整脚本。你也可以直接下载我整理好的版本:
# ============================================
# PowerShell 安全响应头 CSV 导出报告脚本
# 作者:youres | https://www.youres.cn
# ============================================
$urls = @(
\"https://www.youres.cn\",
\"https://www.baidu.com\",
\"https://www.zhihu.com\"
)
function Get-SecurityHeaderScore {
param($headersObj)
$score = 0
if ($headersObj.StrictTransportSecurity -and $headersObj.StrictTransportSecurity -ne 'ERROR') { $score += 20 }
if ($headersObj.XContentTypeOptions -eq 'nosniff') { $score += 10 }
if ($headersObj.XFrameOptions -in 'DENY','SAMEORIGIN') { $score += 10 }
if ($headersObj.XXSSProtection -eq '1; mode=block') { $score += 10 }
if ($headersObj.ContentSecurityPolicy -and $headersObj.ContentSecurityPolicy -ne 'ERROR') { $score += 30 }
if ($headersObj.ReferrerPolicy -and $headersObj.ReferrerPolicy -ne 'ERROR') { $score += 10 }
if ($headersObj.PermissionsPolicy -and $headersObj.PermissionsPolicy -ne 'ERROR') { $score += 10 }
$grade = if ($score -ge 80) { 'A' } elseif ($score -ge 60) { 'B' } elseif ($score -ge 40) { 'C' } elseif ($score -ge 20) { 'D' } else { 'F' }
return @{ Score = $score; Grade = $grade }
}
$results = @()
foreach ($url in $urls) {
try {
$resp = Invoke-WebRequest -Uri $url -UseBasicParsing -TimeoutSec 10
$h = $resp.Headers
$obj = [PSCustomObject]@{
URL = $url
StrictTransportSecurity = $h['Strict-Transport-Security'] -join ','
XContentTypeOptions = $h['X-Content-Type-Options'] -join ','
XFrameOptions = $h['X-Frame-Options'] -join ','
XXSSProtection = $h['X-XSS-Protection'] -join ','
ContentSecurityPolicy = $h['Content-Security-Policy'] -join ','
ReferrerPolicy = $h['Referrer-Policy'] -join ','
PermissionsPolicy = $h['Permissions-Policy'] -join ','
StatusCode = $resp.StatusCode
CheckTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
}
} catch {
$obj = [PSCustomObject]@{
URL = $url
StrictTransportSecurity = 'ERROR'
XContentTypeOptions = 'ERROR'
XFrameOptions = 'ERROR'
XXSSProtection = 'ERROR'
ContentSecurityPolicy = 'ERROR'
ReferrerPolicy = 'ERROR'
PermissionsPolicy = 'ERROR'
StatusCode = 0
CheckTime = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'
}
}
$results += $obj
}
$report = @()
foreach ($r in $results) {
$g = Get-SecurityHeaderScore -headersObj $r
$report += [PSCustomObject]@{
检测网址 = $r.URL
HSTS = if($r.StrictTransportSecurity -and $r.StrictTransportSecurity -ne 'ERROR') { '已配置' } else { '缺失' }
XContentTypeOptions = if($r.XContentTypeOptions -eq 'nosniff') { 'nosniff' } else { '缺失' }
XFrameOptions = if($r.XFrameOptions -in 'DENY','SAMEORIGIN') { $r.XFrameOptions } else { '缺失' }
XXSSProtection = if($r.XXSSProtection -eq '1; mode=block') { '已配置' } else { '缺失' }
ContentSecurityPolicy = if($r.ContentSecurityPolicy -and $r.ContentSecurityPolicy -ne 'ERROR') { '已配置' } else { '缺失' }
ReferrerPolicy = if($r.ReferrerPolicy -and $r.ReferrerPolicy -ne 'ERROR') { $r.ReferrerPolicy } else { '缺失' }
安全评分 = $g.Score
评级 = $g.Grade
检测时间 = $r.CheckTime
}
}
$outFile = \".\\SecurityHeaderReport_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv\"
$report | Export-Csv -Path $outFile -Encoding UTF8 -NoTypeInformation
Write-Host \"✅ 报告已生成:$outFile\"
$report | Format-Table
五、CSV 报告在 Excel 里打开不乱码的方法
即使用了 -Encoding UTF8,Excel 有时候还是乱码。两个解决方法:
- 用 Excel「数据」→「从文本/CSV 导入」,编码选 UTF-8,不要直接双击打开。
- 在 CSV 文件头部加 BOM:把
Export-Csv -Encoding UTF8改成用StreamWriter手写 BOM,Excel 就能自动识别。
# 带 BOM 的 CSV 导出方法(Excel 双击打开不乱码)
$outFile = \".\\SecurityHeaderReport_BOM_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv\"
$stream = [System.IO.StreamWriter]::new($outFile, $false, [System.Text.UTF8Encoding]::new($true))
$stream.WriteLine(\"检测网址,HSTS,XContentTypeOptions,XFrameOptions,XXSSProtection,ContentSecurityPolicy,ReferrerPolicy,安全评分,评级,检测时间\")
foreach ($row in $report) {
$stream.WriteLine(\"$($row.检测网址),$($row.HSTS),$($row.XContentTypeOptions),$($row.XFrameOptions),$($row.XXSSProtection),$($row.ContentSecurityPolicy),$($row.ReferrerPolicy),$($row.安全评分),$($row.评级),$($row.检测时间)\")
}
$stream.Close()
Write-Host \"✅ BOM CSV 已生成:$outFile(Excel 双击直接打开)\"
六、定时自动跑 + 邮件发送报告
配合 Windows 任务计划程序,每天自动跑一次,把 CSV 报告邮件发给你:
# 在脚本末尾加这段,自动发邮件(需要配置 SMTP)
$smtp = @{
To = 'admin@example.com'
From = 'monitor@example.com'
Subject = \"网站安全响应头日报 $(Get-Date -Format 'yyyy-MM-dd')\"
Body = '附件是最新安全响应头检测报告,请查收。'
SmtpServer = 'smtp.example.com'
Port = 25
Attachments = $outFile
}
Send-MailMessage @smtp
详细邮件告警配置可以参考我之前写的:PowerShell 邮件告警自动化脚本、Send-MailMessage 匿名SMTP 配置教程。
总结
一套 PowerShell 脚本,完成安全头检测 → 评分 → CSV 导出 → 邮件发送的完整闭环。比手动登 Security Headers 网站一个个查效率高 10 倍,而且可以批量、可以定时、可以存档对比。
下次等保测评或者安全巡检,直接把 CSV 报告甩出来,专业度直接拉满。
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论