Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable X-XSS-Protection filter, for safety #26

Merged
merged 1 commit into from
Apr 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 5 additions & 39 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ The default factory will add default values to your site for Content-Security-Po
x-content-type-options: nosniff
content-security-policy: default-src 'self'
x-frame-options: DENY
x-xss-protection: 1; mode=block
x-xss-protection: 0
```

***Note:*** You should ensure you set the security headers as the first middleware in your `Middlewares` (i.e., the first middleware to be applied to responses) to make sure the headers get added to all responses.
Expand All @@ -94,7 +94,7 @@ application.middleware.use(securityHeadersFactory.build())
x-content-type-options: nosniff
content-security-policy: default-src 'none'; script-src https://static.brokenhands.io;
x-frame-options: DENY
x-xss-protection: 1; mode=block
x-xss-protection: 0
```

Each different header has its own configuration and options, details of which can be found below.
Expand All @@ -114,7 +114,7 @@ application.middleware.use(securityHeaders.build())
x-content-type-options: nosniff
content-security-policy: default-src 'none'
x-frame-options: DENY
x-xss-protection: 1; mode=block
x-xss-protection: 0
```

# Server Configuration
Expand Down Expand Up @@ -317,44 +317,10 @@ The [above blog post](https://scotthelme.co.uk/content-security-policy-an-introd

## X-XSS-Protection

X-XSS-Protection configures the browser's cross-site scripting filter. The recommended, and default, setting is `.block` which blocks the response if the browser detects an attack. This can be configured with:

```swift
let xssProtectionConfig = XSSProtectionConfiguration(option: .block)

let securityHeadersFactory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
```

```http
x-xss-protection: 1; mode=block
```

To just enable the protection:

```swift
let xssProtectionConfig = XSSProtectionConfiguration(option: .enable)
```

```http
x-xss-protection: 1
```

To sanitize the page and report the violation:

```swift
let xssProtectionConfig = XSSProtectionConfiguration(option: .report(uri: "https://report-uri.com"))

let securityHeadersFactory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
```

```http
x-xss-protection: 1; report=https://report-uri.com
```

Or to disable:
X-XSS-Protection configures the browser's cross-site scripting filter. This package configures the header to be disabled, which (surprisingly) offers security benefits. See [this article on MDN for more information](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection).

```swift
let xssProtectionConfig = XSSProtectionConfiguration(option: .disable)
let xssProtectionConfig = XSSProtectionConfiguration()

let securityHeadersFactory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
```
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,9 @@
import Vapor

public struct XSSProtectionConfiguration: SecurityHeaderConfiguration {

public enum Options {
case disable
case enable
case block
case report(uri: String)
}

private let option: Options

public init(option: Options) {
self.option = option
}
public init () {}

func setHeader(on response: Response, from request: Request) {
switch option {
case .disable:
response.headers.replaceOrAdd(name: .xssProtection, value: "0")
case .enable:
response.headers.replaceOrAdd(name: .xssProtection, value: "1")
case .block:
response.headers.replaceOrAdd(name: .xssProtection, value: "1; mode=block")
case .report(let uri):
response.headers.replaceOrAdd(name: .xssProtection, value: "1; report=\(uri)")
}
response.headers.replaceOrAdd(name: .xssProtection, value: "0")
}
}
2 changes: 1 addition & 1 deletion Sources/VaporSecurityHeaders/SecurityHeaders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public struct SecurityHeaders {
init(contentTypeConfiguration: ContentTypeOptionsConfiguration = ContentTypeOptionsConfiguration(option: .nosniff),
contentSecurityPolicyConfiguration: ContentSecurityPolicyConfiguration = ContentSecurityPolicyConfiguration(value: ContentSecurityPolicy().defaultSrc(sources: CSPKeywords.`self`)),
frameOptionsConfiguration: FrameOptionsConfiguration = FrameOptionsConfiguration(option: .deny),
xssProtectionConfiguration: XSSProtectionConfiguration = XSSProtectionConfiguration(option: .block),
xssProtectionConfiguration: XSSProtectionConfiguration = XSSProtectionConfiguration(),
hstsConfiguration: StrictTransportSecurityConfiguration? = nil,
serverConfiguration: ServerConfiguration? = nil,
contentSecurityPolicyReportOnlyConfiguration: ContentSecurityPolicyReportOnlyConfiguration? = nil,
Expand Down
2 changes: 1 addition & 1 deletion Sources/VaporSecurityHeaders/SecurityHeadersFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ public class SecurityHeadersFactory {
var contentTypeOptions = ContentTypeOptionsConfiguration(option: .nosniff)
var contentSecurityPolicy = ContentSecurityPolicyConfiguration(value: ContentSecurityPolicy().defaultSrc(sources: CSPKeywords.`self`))
var frameOptions = FrameOptionsConfiguration(option: .deny)
var xssProtection = XSSProtectionConfiguration(option: .block)
var xssProtection = XSSProtectionConfiguration()
var hsts: StrictTransportSecurityConfiguration?
var server: ServerConfiguration?
var referrerPolicy: ReferrerPolicyConfiguration?
Expand Down
42 changes: 9 additions & 33 deletions Tests/VaporSecurityHeadersTests/HeaderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class HeaderTests: XCTestCase {
let expectedXCTOHeaderValue = "nosniff"
let expectedCSPHeaderValue = "default-src 'self'"
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"

let response = try makeTestResponse(for: request, securityHeadersToAdd: SecurityHeadersFactory())

Expand All @@ -51,7 +51,7 @@ class HeaderTests: XCTestCase {
let expectedXCTOHeaderValue = "nosniff"
let expectedCSPHeaderValue = "default-src 'self'"
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"
let expectedHSTSHeaderValue = "max-age=31536000; includeSubDomains; preload"

let response = try makeTestResponse(for: request, securityHeadersToAdd: SecurityHeadersFactory().with(strictTransportSecurity: StrictTransportSecurityConfiguration()))
Expand All @@ -67,7 +67,7 @@ class HeaderTests: XCTestCase {
let expectedXCTOHeaderValue = "nosniff"
let expectedCSPHeaderValue = "default-src 'none'"
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"

let response = try makeTestResponse(for: request, securityHeadersToAdd: SecurityHeadersFactory.api())

Expand All @@ -81,7 +81,7 @@ class HeaderTests: XCTestCase {
let expectedXCTOHeaderValue = "nosniff"
let expectedCSPHeaderValue = "default-src 'none'"
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"
let expectedHSTSHeaderValue = "max-age=31536000; includeSubDomains; preload"

let response = try makeTestResponse(for: request, securityHeadersToAdd: SecurityHeadersFactory.api().with(strictTransportSecurity: StrictTransportSecurityConfiguration()))
Expand Down Expand Up @@ -133,38 +133,14 @@ class HeaderTests: XCTestCase {
XCTAssertEqual("ALLOW-FROM https://test.com", response.headers[.xFrameOptions].first)
}

func testHeaderWithXssProtectionDisable() throws {
let xssProtectionConfig = XSSProtectionConfiguration(option: .disable)
func testHeaderWithXssProtection() throws {
let xssProtectionConfig = XSSProtectionConfiguration()
let factory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
let response = try makeTestResponse(for: request, securityHeadersToAdd: factory)

XCTAssertEqual("0", response.headers[.xssProtection].first)
}

func testHeaderWithXssProtectionEnable() throws {
let xssProtectionConfig = XSSProtectionConfiguration(option: .enable)
let factory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
let response = try makeTestResponse(for: request, securityHeadersToAdd: factory)

XCTAssertEqual("1", response.headers[.xssProtection].first)
}

func testHeaderWithXssProtectionBlock() throws {
let xssProtectionConfig = XSSProtectionConfiguration(option: .block)
let factory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
let response = try makeTestResponse(for: request, securityHeadersToAdd: factory)

XCTAssertEqual("1; mode=block", response.headers[.xssProtection].first)
}

func testHeaderWithXssProtectionReport() throws {
let xssProtectionConfig = XSSProtectionConfiguration(option: .report(uri: "https://test.com"))
let factory = SecurityHeadersFactory().with(XSSProtection: xssProtectionConfig)
let response = try makeTestResponse(for: request, securityHeadersToAdd: factory)

XCTAssertEqual("1; report=https://test.com", response.headers[.xssProtection].first)
}

func testHeaderWithHSTSwithMaxAge() throws {
let hstsConfig = StrictTransportSecurityConfiguration(maxAge: 30)
let factory = SecurityHeadersFactory().with(strictTransportSecurity: hstsConfig)
Expand Down Expand Up @@ -591,7 +567,7 @@ class HeaderTests: XCTestCase {
let expectedXCTOHeaderValue = "nosniff"
let expectedCSPHeaderValue = "default-src 'none'"
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"

let response = try makeTestResponse(for: abortRequest, securityHeadersToAdd: SecurityHeadersFactory.api())

Expand All @@ -605,7 +581,7 @@ class HeaderTests: XCTestCase {
let expectedXCTOHeaderValue = "nosniff"
let expectedCSPHeaderValue = "default-src 'none'"
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"
let response = try makeTestResponse(for: fileRequest, securityHeadersToAdd: SecurityHeadersFactory.api(), fileMiddleware: StubFileMiddleware())

XCTAssertEqual("Hello World!", String(data: response.body.data!, encoding: String.Encoding.utf8))
Expand All @@ -622,7 +598,7 @@ class HeaderTests: XCTestCase {
.defaultSrc(sources: CSPKeywords.none)
.scriptSrc(sources: "test")
let expectedXFOHeaderValue = "DENY"
let expectedXSSProtectionHeaderValue = "1; mode=block"
let expectedXSSProtectionHeaderValue = "0"

let response = try makeTestResponse(for: fileRequest, securityHeadersToAdd: SecurityHeadersFactory.api(), fileMiddleware: StubFileMiddleware(cspConfig: ContentSecurityPolicyConfiguration(value: csp)))

Expand Down