Skip to content

Commit

Permalink
Merge pull request #1409 from Badgerati/missing-csp-params
Browse files Browse the repository at this point in the history
Adds new/missing CSP parmeters in security headers
  • Loading branch information
Badgerati authored Oct 16, 2024
2 parents bd7e284 + 326c777 commit 696cc43
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 50 deletions.
69 changes: 36 additions & 33 deletions docs/Tutorials/Middleware/Types/Security.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

The security headers middleware runs at the beginning of every request, and if any security headers are defined they will be added onto the response.

The following headers are currently supported, but you can add custom header values:
The following headers are currently supported, but you can add custom header values via [`Add-PodeSecurityHeader`](../../../../Functions/Security/Add-PodeSecurityHeader) for any missing:

* Access-Control-Max-Age
* Access-Control-Allow-Methods
Expand All @@ -13,6 +13,7 @@ The following headers are currently supported, but you can add custom header val
* Cross-Origin-Opener-Policy
* Strict-Transport-Security
* Content-Security-Policy
* Content-Security-Policy-Report-Only
* X-XSS-Protection
* Permissions-Policy
* X-Frame-Options
Expand All @@ -37,44 +38,44 @@ To remove all configured values, use [`Remove-PodeSecurity`](../../../../Functio

The following values are used for each header when the `Simple` type is supplied:

| Name | Value |
| ---- | ----- |
| Access-Control-Max-Age | 7200 |
| Access-Control-Allow-Origin | * |
| Access-Control-Allow-Methods | * |
| Access-Control-Allow-Headers | * |
| Cross-Origin-Embedder-Policy | require-corp |
| Cross-Origin-Resource-Policy | same-origin |
| Cross-Origin-Opener-Policy | same-origin |
| Content-Security-Policy | default-src 'self' |
| X-XSS-Protection | 0 |
| Permissions-Policy | accelerometer=(), autoplay=(self), camera=(), display-capture=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(), payment=(), picture-in-picture=(self), sync-xhr=(), usb=() |
| X-Frame-Options | SAMEORIGIN |
| X-Content-Type-Options | nosniff |
| Referred-Policy | strict-origin |
| Name | Value |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Access-Control-Max-Age | 7200 |
| Access-Control-Allow-Origin | * |
| Access-Control-Allow-Methods | * |
| Access-Control-Allow-Headers | * |
| Cross-Origin-Embedder-Policy | require-corp |
| Cross-Origin-Resource-Policy | same-origin |
| Cross-Origin-Opener-Policy | same-origin |
| Content-Security-Policy | default-src 'self' |
| X-XSS-Protection | 0 |
| Permissions-Policy | accelerometer=(), autoplay=(self), camera=(), display-capture=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(), payment=(), picture-in-picture=(self), sync-xhr=(), usb=() |
| X-Frame-Options | SAMEORIGIN |
| X-Content-Type-Options | nosniff |
| Referred-Policy | strict-origin |

The Server header is also hidden.

### Strict

The following values are used for each header when the `Strict` type is supplied:

| Name | Value |
| ---- | ----- |
| Access-Control-Max-Age | 7200 |
| Access-Control-Allow-Methods | * |
| Access-Control-Allow-Origin | * |
| Access-Control-Allow-Headers | * |
| Cross-Origin-Embedder-Policy | require-corp |
| Cross-Origin-Resource-Policy | same-origin |
| Cross-Origin-Opener-Policy | same-origin |
| Strict-Transport-Security | max-age=31536000; includeSubDomains |
| Content-Security-Policy | default-src 'self' |
| X-XSS-Protection | 0 |
| Permissions-Policy | accelerometer=(), autoplay=(self), camera=(), display-capture=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(), payment=(), picture-in-picture=(self), sync-xhr=(), usb=() |
| X-Frame-Options | DENY |
| X-Content-Type-Options | nosniff |
| Referred-Policy | no-referrer |
| Name | Value |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Access-Control-Max-Age | 7200 |
| Access-Control-Allow-Methods | * |
| Access-Control-Allow-Origin | * |
| Access-Control-Allow-Headers | * |
| Cross-Origin-Embedder-Policy | require-corp |
| Cross-Origin-Resource-Policy | same-origin |
| Cross-Origin-Opener-Policy | same-origin |
| Strict-Transport-Security | max-age=31536000; includeSubDomains |
| Content-Security-Policy | default-src 'self' |
| X-XSS-Protection | 0 |
| Permissions-Policy | accelerometer=(), autoplay=(self), camera=(), display-capture=(self), fullscreen=(self), geolocation=(self), gyroscope=(self), magnetometer=(self), microphone=(), payment=(), picture-in-picture=(self), sync-xhr=(), usb=() |
| X-Frame-Options | DENY |
| X-Content-Type-Options | nosniff |
| Referred-Policy | no-referrer |

The Server header is also hidden.

Expand Down Expand Up @@ -153,12 +154,14 @@ The following functions exist:
* [`Set-PodeSecurityContentSecurityPolicy`](../../../../Functions/Security/Set-PodeSecurityContentSecurityPolicy)
* [`Remove-PodeSecurityContentSecurityPolicy`](../../../../Functions/Security/Remove-PodeSecurityContentSecurityPolicy)

The `Content-Security-Policy` header controls a whitelist of approved sourced from which the browser can load resoures. For example:
The `Content-Security-Policy` header controls a whitelist of approved sources from which the browser can load resources. For example:

```powershell
Set-PodeSecurityContentSecurityPolicy -Default 'self' -Image 'self', 'data'
```

By supplying the `-ReportOnly` switch, the `Content-Security-Policy-Report-Only` header will be used instead.

### Permissions Policy

The following functions exist:
Expand Down
30 changes: 21 additions & 9 deletions pode.build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -799,41 +799,53 @@ Task SetupPowerShell {
osx = "powershell-$($PowerShellVersion)-$($os)-$($arch).tar.gz"
})[$os]

# build the blob name
$blobName = "v$($PowerShellVersion -replace '\.', '-')"
# build the URL
$urls = @{
Old = "https://pscoretestdata.blob.core.windows.net/v$($PowerShellVersion -replace '\.', '-')/$($packageName)"
New = "https://powershellinfraartifacts-gkhedzdeaghdezhr.z01.azurefd.net/install/v$($PowerShellVersion)/$($packageName)"
}

# download the package to a temp location
$outputFile = Join-Path -Path ([System.IO.Path]::GetTempPath()) -ChildPath $packageName
$downloadParams = @{
Uri = "https://pscoretestdata.blob.core.windows.net/$($blobName)/$($packageName)"
Uri = $urls.New
OutFile = $outputFile
ErrorAction = 'Stop'
}

Write-Host "Downloading $($packageName) from $($downloadParams.Uri)"
Write-Host "Output file: $($outputFile)"

# retry the download 3 times, with a sleep of 10s between each attempt
# retry the download 6 times, with a sleep of 10s between each attempt, and altering between old and new URLs
$counter = 0
$success = $false

do {
try {
$counter++
Write-Host "Attempt $($counter) of 3"
Write-Host "Attempt $($counter) of 6"

# use new URL for odd attempts, and old URL for even attempts
if ($counter % 2 -eq 0) {
$downloadParams.Uri = $urls.Old
}
else {
$downloadParams.Uri = $urls.New
}

# download the package
Write-Host "Attempting download of $($packageName) from $($downloadParams.Uri)"
Invoke-WebRequest @downloadParams

$success = $true
Write-Host "Downloaded $($packageName) successfully"
}
catch {
$success = $false
if ($counter -ge 3) {
throw "Failed to download PowerShell package after 3 attempts. Error: $($_.Exception.Message)"
if ($counter -ge 6) {
throw "Failed to download PowerShell package after 6 attempts. Error: $($_.Exception.Message)"
}

Start-Sleep -Seconds 10
Start-Sleep -Seconds 5
}
} while (!$success)

Expand Down
38 changes: 35 additions & 3 deletions src/Private/Security.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -1037,18 +1037,32 @@ function Protect-PodeContentSecurityKeyword {
$Name = $Name.ToLowerInvariant()

$keywords = @(
# standard keywords
'none',
'self',
'strict-dynamic',
'report-sample',
'inline-speculation-rules',

# unsafe keywords
'unsafe-inline',
'unsafe-eval'
'unsafe-eval',
'unsafe-hashes',
'wasm-unsafe-eval'
)

$schemes = @(
'http',
'https',
'data',
'blob',
'filesystem',
'mediastream',
'ws',
'wss',
'data',
'ftp',
'mailto',
'tel',
'file'
)

Expand Down Expand Up @@ -1183,8 +1197,20 @@ function Set-PodeSecurityContentSecurityPolicyInternal {
Protect-PodeContentSecurityKeyword -Name 'base-uri' -Value $Params.BaseUri -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'form-action' -Value $Params.FormAction -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'frame-ancestors' -Value $Params.FrameAncestor -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'fenched-frame-src' -Value $Params.FencedFrame -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'prefetch-src' -Value $Params.Prefetch -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'script-src-attr' -Value $Params.ScriptAttr -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'script-src-elem' -Value $Params.ScriptElem -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'style-src-attr' -Value $Params.StyleAttr -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'style-src-elem' -Value $Params.StyleElem -Append:$Append
Protect-PodeContentSecurityKeyword -Name 'worker-src' -Value $Params.Worker -Append:$Append
)

# add "report-uri" if supplied
if (![string]::IsNullOrWhiteSpace($Params.ReportUri)) {
$values += "report-uri $($Params.ReportUri)".Trim()
}

if (![string]::IsNullOrWhiteSpace($Params.Sandbox) -and ($Params.Sandbox -ine 'None')) {
$values += "sandbox $($Params.Sandbox.ToLowerInvariant())".Trim()
}
Expand All @@ -1202,7 +1228,13 @@ function Set-PodeSecurityContentSecurityPolicyInternal {

# Add the Content Security Policy header to the response or relevant context. This cmdlet
# sets the HTTP header with the name 'Content-Security-Policy' and the constructed value.
Add-PodeSecurityHeader -Name 'Content-Security-Policy' -Value $value
# if ReportOnly is set, the header name is set to 'Content-Security-Policy-Report-Only'.
$header = 'Content-Security-Policy'
if ($Params.ReportOnly) {
$header = 'Content-Security-Policy-Report-Only'
}

Add-PodeSecurityHeader -Name $header -Value $value

# this is done to explicitly disable XSS auditors in modern browsers
# as having it enabled has now been found to cause more vulnerabilities
Expand Down
Loading

0 comments on commit 696cc43

Please sign in to comment.