Make encoded character options opt-in
This commit is contained in:
parent
ee265a8509
commit
adf47fba31
19 changed files with 221 additions and 179 deletions
|
|
@ -87,10 +87,10 @@ Complete documentation is available at https://traefik.io`,
|
|||
func runCmd(staticConfiguration *static.Configuration) error {
|
||||
configureLogging(staticConfiguration)
|
||||
|
||||
// Display warning to advertise for new behavior of rejecting encoded characters in the request path.
|
||||
// Deprecated: this has to be removed in the next minor/major version.
|
||||
log.WithoutContext().Warnf("Starting with v2.11.32, Traefik now rejects some encoded characters in the request path by default. " +
|
||||
"Refer to the documentation for more details: https://doc.traefik.io/traefik/v2.11/migration/v2/#encoded-characters-in-request-path")
|
||||
log.WithoutContext().Warn("Traefik can reject some encoded characters in the request path." +
|
||||
"When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986)," +
|
||||
"it is recommended to set these options to `false` to avoid split-view situation." +
|
||||
"Refer to the documentation for more details: https://doc.traefik.io/traefik/v2.11/migration/v2/#encoded-characters-configuration-default-values")
|
||||
|
||||
http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
|
||||
|
||||
|
|
|
|||
|
|
@ -737,3 +737,30 @@ Note: This check is not done against query parameters,
|
|||
but only against the request path as defined in [RFC3986 section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3).
|
||||
|
||||
Please check out the entrypoint [encodedCharacters option](../routing/entrypoints.md#encoded-characters) documentation for more details.
|
||||
|
||||
## v2.11.35
|
||||
|
||||
### Encoded Characters Configuration Default Values
|
||||
|
||||
Since `v2.11.35`, the options for encoded characters now have a `true` default value.
|
||||
This means that Traefik will not reject requests with a path containing a specific set of encoded characters by default.
|
||||
It is now up to the users to configure the security hardening of encoded characters.
|
||||
|
||||
Here is the list of the encoded characters that can be configured to `false` to disallow them:
|
||||
|
||||
| Encoded Character | Character | Config options | Default value |
|
||||
|-------------------|-------------------------|--------------------------------------------------------------------------------------|---------------|
|
||||
| `%2f` or `%2F` | `/` (slash) | `entryPoints.<name>`<br/>`.http.encodedCharacters`<br/>`.allowEncodedSlash` | `true` |
|
||||
| `%5c` or `%5C` | `\` (backslash) | `entryPoints.<name>.`<br/>`.http.encodedCharacters`<br/>`.allowEncodedBackSlash` | `true` |
|
||||
| `%00` | `NULL` (null character) | `entryPoints.<name>.`<br/>`.http.encodedCharacters`<br/>`.allowEncodedNullCharacter` | `true` |
|
||||
| `%3b` or `%3B` | `;` (semicolon) | `entryPoints.<name>.`<br/>`.http.encodedCharacters`<br/>`.allowEncodedSemicolon` | `true` |
|
||||
| `%25` | `%` (percent) | `entryPoints.<name>.`<br/>`.http.encodedCharacters`<br/>`.allowEncodedPercent` | `true` |
|
||||
| `%3f` or `%3F` | `?` (question mark) | `entryPoints.<name>.`<br/>`.http.encodedCharacters`<br/>`.allowEncodedQuestionMark` | `true` |
|
||||
| `%23` | `#` (hash) | `entryPoints.<name>.`<br/>`.http.encodedCharacters`<br/>`.allowEncodedHash` | `true` |
|
||||
|
||||
Note: This check is not done against query parameters,
|
||||
but only against the request path as defined
|
||||
in [RFC3986 section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3).
|
||||
|
||||
Please check out the entrypoint [encodedCharacters option](../routing/entrypoints.md#encoded-characters) documentation
|
||||
for more details.
|
||||
|
|
|
|||
|
|
@ -124,25 +124,25 @@ Trust only forwarded headers from selected IPs.
|
|||
HTTP configuration.
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodedbackslash`:
|
||||
Defines whether requests with encoded back slash characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded back slash characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodedhash`:
|
||||
Defines whether requests with encoded hash characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded hash characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodednullcharacter`:
|
||||
Defines whether requests with encoded null characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded null characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodedpercent`:
|
||||
Defines whether requests with encoded percent characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded percent characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodedquestionmark`:
|
||||
Defines whether requests with encoded question mark characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded question mark characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodedsemicolon`:
|
||||
Defines whether requests with encoded semicolon characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded semicolon characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodedcharacters.allowencodedslash`:
|
||||
Defines whether requests with encoded slash characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded slash characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`--entrypoints.<name>.http.encodequerysemicolons`:
|
||||
Defines whether request query semicolons should be URLEncoded. (Default: ```false```)
|
||||
|
|
|
|||
|
|
@ -133,25 +133,25 @@ HTTP/3 configuration. (Default: ```false```)
|
|||
UDP port to advertise, on which HTTP/3 is available. (Default: ```0```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDBACKSLASH`:
|
||||
Defines whether requests with encoded back slash characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded back slash characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDHASH`:
|
||||
Defines whether requests with encoded hash characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded hash characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDNULLCHARACTER`:
|
||||
Defines whether requests with encoded null characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded null characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDPERCENT`:
|
||||
Defines whether requests with encoded percent characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded percent characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDQUESTIONMARK`:
|
||||
Defines whether requests with encoded question mark characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded question mark characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDSEMICOLON`:
|
||||
Defines whether requests with encoded semicolon characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded semicolon characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEDCHARACTERS_ALLOWENCODEDSLASH`:
|
||||
Defines whether requests with encoded slash characters in the path are allowed. (Default: ```false```)
|
||||
Defines whether requests with encoded slash characters in the path are allowed. (Default: ```true```)
|
||||
|
||||
`TRAEFIK_ENTRYPOINTS_<NAME>_HTTP_ENCODEQUERYSEMICOLONS`:
|
||||
Defines whether request query semicolons should be URLEncoded. (Default: ```false```)
|
||||
|
|
|
|||
|
|
@ -129,13 +129,13 @@ They can be defined by using a file (YAML or TOML) or CLI arguments.
|
|||
- "192.168.0.1"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedBackSlash: true
|
||||
allowEncodedNullCharacter: true
|
||||
allowEncodedSemicolon: true
|
||||
allowEncodedPercent: true
|
||||
allowEncodedQuestionMark: true
|
||||
allowEncodedHash: true
|
||||
allowEncodedSlash: false
|
||||
allowEncodedBackSlash: false
|
||||
allowEncodedNullCharacter: false
|
||||
allowEncodedSemicolon: false
|
||||
allowEncodedPercent: false
|
||||
allowEncodedQuestionMark: false
|
||||
allowEncodedHash: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -162,13 +162,13 @@ They can be defined by using a file (YAML or TOML) or CLI arguments.
|
|||
insecure = true
|
||||
trustedIPs = ["127.0.0.1", "192.168.0.1"]
|
||||
[entryPoints.name.http.encodedCharacters]
|
||||
allowEncodedSlash = true
|
||||
allowEncodedBackSlash = true
|
||||
allowEncodedNullCharacter = true
|
||||
allowEncodedSemicolon = true
|
||||
allowEncodedPercent = true
|
||||
allowEncodedQuestionMark = true
|
||||
allowEncodedHash = true
|
||||
allowEncodedSlash = false
|
||||
allowEncodedBackSlash = false
|
||||
allowEncodedNullCharacter = false
|
||||
allowEncodedSemicolon = false
|
||||
allowEncodedPercent = false
|
||||
allowEncodedQuestionMark = false
|
||||
allowEncodedHash = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
|
|
@ -185,13 +185,13 @@ They can be defined by using a file (YAML or TOML) or CLI arguments.
|
|||
--entryPoints.name.proxyProtocol.trustedIPs=127.0.0.1,192.168.0.1
|
||||
--entryPoints.name.forwardedHeaders.insecure=true
|
||||
--entryPoints.name.forwardedHeaders.trustedIPs=127.0.0.1,192.168.0.1
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedSlash=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedBackSlash=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedNullCharacter=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedSemicolon=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedPercent=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedQuestionMark=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedHash=true
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedSlash=false
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedBackSlash=false
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedNullCharacter=false
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedSemicolon=false
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedPercent=false
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedQuestionMark=false
|
||||
--entryPoints.name.http.encodedCharacters.allowEncodedHash=false
|
||||
```
|
||||
|
||||
### Address
|
||||
|
|
@ -1021,20 +1021,21 @@ entryPoints:
|
|||
### Encoded Characters
|
||||
|
||||
You can configure Traefik to control the handling of encoded characters in request paths for security purposes.
|
||||
By default, Traefik rejects requests with path containing certain encoded characters that could be used in path traversal or other security attacks.
|
||||
By default, Traefik do not reject requests with path containing certain encoded characters that could be used in path traversal or other security attacks.
|
||||
|
||||
!!! info
|
||||
|
||||
This check is not done against the request query parameters,
|
||||
but only against the request path as defined in [RFC3986 section-3](https://datatracker.ietf.org/doc/html/rfc3986#section-3).
|
||||
|
||||
!!! warning "Security Considerations"
|
||||
!!! info "Security Considerations"
|
||||
|
||||
Allowing certain encoded characters may expose your application to security vulnerabilities.
|
||||
When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and notably decode encoded reserved characters in the requets path,
|
||||
it is recommended to set these options to `false` to avoid split-view situation and helps prevent path traversal attacks or other malicious attempts to bypass security controls.
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedSlash`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded slash characters (`%2F` or `%2f`) in the path are allowed.
|
||||
|
||||
|
|
@ -1045,7 +1046,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: true
|
||||
allowEncodedSlash: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1055,18 +1056,18 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedSlash = true
|
||||
allowEncodedSlash = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedSlash=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedSlash=false
|
||||
```
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedBackSlash`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded back slash characters (`%5C` or `%5c`) in the path are allowed.
|
||||
|
||||
|
|
@ -1077,7 +1078,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedBackSlash: true
|
||||
allowEncodedBackSlash: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1087,18 +1088,18 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedBackSlash = true
|
||||
allowEncodedBackSlash = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedBackSlash=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedBackSlash=false
|
||||
```
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedNullCharacter`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded null characters (`%00`) in the path are allowed.
|
||||
|
||||
|
|
@ -1109,7 +1110,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedNullCharacter: true
|
||||
allowEncodedNullCharacter: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1119,18 +1120,18 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedNullCharacter = true
|
||||
allowEncodedNullCharacter = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedNullCharacter=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedNullCharacter=false
|
||||
```
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedSemicolon`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded semicolon characters (`%3B` or `%3b`) in the path are allowed.
|
||||
|
||||
|
|
@ -1141,7 +1142,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSemicolon: true
|
||||
allowEncodedSemicolon: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1151,18 +1152,18 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedSemicolon = true
|
||||
allowEncodedSemicolon = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedSemicolon=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedSemicolon=false
|
||||
```
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedPercent`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded percent characters (`%25`) in the path are allowed.
|
||||
|
||||
|
|
@ -1173,7 +1174,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedPercent: true
|
||||
allowEncodedPercent: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1183,18 +1184,18 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedPercent = true
|
||||
allowEncodedPercent = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedPercent=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedPercent=false
|
||||
```
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedQuestionMark`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded question mark characters (`%3F` or `%3f`) in the path are allowed.
|
||||
|
||||
|
|
@ -1205,7 +1206,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedQuestionMark: true
|
||||
allowEncodedQuestionMark: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1215,18 +1216,18 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedQuestionMark = true
|
||||
allowEncodedQuestionMark = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedQuestionMark=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedQuestionMark=false
|
||||
```
|
||||
|
||||
??? info "`encodedCharacters.allowEncodedHash`"
|
||||
|
||||
_Optional, Default=false_
|
||||
_Optional, Default=true_
|
||||
|
||||
Controls whether requests with encoded hash characters (`%23`) in the path are allowed.
|
||||
|
||||
|
|
@ -1237,7 +1238,7 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address: ":80"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedHash: true
|
||||
allowEncodedHash: false
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
@ -1247,13 +1248,13 @@ By default, Traefik rejects requests with path containing certain encoded charac
|
|||
address = ":80"
|
||||
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedHash = true
|
||||
allowEncodedHash = false
|
||||
```
|
||||
|
||||
```bash tab="CLI"
|
||||
## Static configuration
|
||||
--entryPoints.web.address=:80
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedHash=true
|
||||
--entryPoints.web.http.encodedCharacters.allowEncodedHash=false
|
||||
```
|
||||
|
||||
### SanitizePath
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ When Traefik receives an HTTP request, it processes the request path through sev
|
|||
|
||||
Traefik inspects the path for potentially dangerous encoded characters and rejects requests containing them unless explicitly allowed.
|
||||
|
||||
Here is the list of the encoded characters that are rejected by default:
|
||||
Here is the list of the encoded characters that are allowed by default:
|
||||
|
||||
| Encoded Character | Character |
|
||||
|-------------------|-------------------------|
|
||||
|
|
@ -87,7 +87,12 @@ Configure it in the [EntryPoints](../routing/entrypoints.md#encoded-characters)
|
|||
|
||||
This filtering occurs before path sanitization and catches attack attempts that use encoding to bypass other security controls.
|
||||
|
||||
All encoded character filtering is enabled by default (`false` means encoded characters are rejected), providing maximum security:
|
||||
All encoded character filtering is disabled by default (`true` means encoded characters are allowed).
|
||||
|
||||
!!! info "Security Considerations"
|
||||
|
||||
When your backend is not fully compliant with [RFC 3986](https://datatracker.ietf.org/doc/html/rfc3986) and notably decode encoded reserved characters in the requets path,
|
||||
it is recommended to set these options to `false` to avoid split-view situation and helps prevent path traversal attacks or other malicious attempts to bypass security controls.
|
||||
|
||||
```yaml tab="File (YAML)"
|
||||
entryPoints:
|
||||
|
|
@ -95,13 +100,13 @@ entryPoints:
|
|||
address: ":443"
|
||||
http:
|
||||
encodedCharacters:
|
||||
allowEncodedSlash: false # %2F - Default: false (RECOMMENDED)
|
||||
allowEncodedBackSlash: false # %5C - Default: false (RECOMMENDED)
|
||||
allowEncodedNullCharacter: false # %00 - Default: false (RECOMMENDED)
|
||||
allowEncodedSemicolon: false # %3B - Default: false (RECOMMENDED)
|
||||
allowEncodedPercent: false # %25 - Default: false (RECOMMENDED)
|
||||
allowEncodedQuestionMark: false # %3F - Default: false (RECOMMENDED)
|
||||
allowEncodedHash: false # %23 - Default: false (RECOMMENDED)
|
||||
allowEncodedSlash: false # %2F - Default: true
|
||||
allowEncodedBackSlash: false # %5C - Default: true
|
||||
allowEncodedNullCharacter: false # %00 - Default: true
|
||||
allowEncodedSemicolon: false # %3B - Default: true
|
||||
allowEncodedPercent: false # %25 - Default: true
|
||||
allowEncodedQuestionMark: false # %3F - Default: true
|
||||
allowEncodedHash: false # %23 - Default: true
|
||||
```
|
||||
|
||||
```toml tab="File (TOML)"
|
||||
|
|
|
|||
|
|
@ -8,12 +8,18 @@
|
|||
[entryPoints]
|
||||
[entryPoints.strict]
|
||||
address = ":8000"
|
||||
# Default: no encoded characters allowed
|
||||
[entryPoints.strict.http.encodedCharacters]
|
||||
allowEncodedSlash = false
|
||||
|
||||
[entryPoints.permissive]
|
||||
address = ":8001"
|
||||
# No config, default values should apply
|
||||
|
||||
[entryPoints.permissive2]
|
||||
address = ":8002"
|
||||
# No config for allowEncodedSlash, default value is effectively true
|
||||
[entryPoints.permissive.http.encodedCharacters]
|
||||
allowEncodedSlash = true
|
||||
allowEncodedBackSlash = false
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@
|
|||
[entryPoints]
|
||||
[entryPoints.web]
|
||||
address = ":8000"
|
||||
[entryPoints.web.http.encodedCharacters]
|
||||
allowEncodedSlash = true
|
||||
|
||||
[api]
|
||||
insecure = true
|
||||
|
|
|
|||
|
|
@ -1520,6 +1520,12 @@ func (s *SimpleSuite) TestEncodedCharactersDifferentEntryPoints() {
|
|||
target: "127.0.0.1:8001", // permissive entry point
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "Encoded slash should be ALLOWED on permissive2 entry point",
|
||||
request: "GET /path%2Fwith%2Fslash HTTP/1.1\r\nHost: test.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8002", // permissive2 entry point
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "Regular path should work on strict entry point",
|
||||
request: "GET /regular/path HTTP/1.1\r\nHost: test.localhost\r\n\r\n",
|
||||
|
|
@ -1532,6 +1538,12 @@ func (s *SimpleSuite) TestEncodedCharactersDifferentEntryPoints() {
|
|||
target: "127.0.0.1:8001",
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
{
|
||||
desc: "Regular path should work on permissive2 entry point",
|
||||
request: "GET /regular/path HTTP/1.1\r\nHost: test.localhost\r\n\r\n",
|
||||
target: "127.0.0.1:8002",
|
||||
expected: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
|
|
|||
|
|
@ -43,14 +43,14 @@ type Service struct {
|
|||
|
||||
// Router holds the router configuration.
|
||||
type Router struct {
|
||||
EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
|
||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
||||
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
||||
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
|
||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
||||
DeniedEncodedPathCharacters RouterDeniedEncodedPathCharacters `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
||||
EntryPoints []string `json:"entryPoints,omitempty" toml:"entryPoints,omitempty" yaml:"entryPoints,omitempty" export:"true"`
|
||||
Middlewares []string `json:"middlewares,omitempty" toml:"middlewares,omitempty" yaml:"middlewares,omitempty" export:"true"`
|
||||
Service string `json:"service,omitempty" toml:"service,omitempty" yaml:"service,omitempty" export:"true"`
|
||||
Rule string `json:"rule,omitempty" toml:"rule,omitempty" yaml:"rule,omitempty"`
|
||||
Priority int `json:"priority,omitempty" toml:"priority,omitempty,omitzero" yaml:"priority,omitempty" export:"true"`
|
||||
TLS *RouterTLSConfig `json:"tls,omitempty" toml:"tls,omitempty" yaml:"tls,omitempty" label:"allowEmpty" file:"allowEmpty" kv:"allowEmpty" export:"true"`
|
||||
DefaultRule bool `json:"-" toml:"-" yaml:"-" label:"-" file:"-"`
|
||||
DeniedEncodedPathCharacters *RouterDeniedEncodedPathCharacters `json:"-" toml:"-" yaml:"-" label:"-" file:"-" kv:"-"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen=true
|
||||
|
|
|
|||
|
|
@ -1035,7 +1035,11 @@ func (in *Router) DeepCopyInto(out *Router) {
|
|||
*out = new(RouterTLSConfig)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
out.DeniedEncodedPathCharacters = in.DeniedEncodedPathCharacters
|
||||
if in.DeniedEncodedPathCharacters != nil {
|
||||
in, out := &in.DeniedEncodedPathCharacters, &out.DeniedEncodedPathCharacters
|
||||
*out = new(RouterDeniedEncodedPathCharacters)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,6 +84,16 @@ type EncodedCharacters struct {
|
|||
AllowEncodedHash bool `description:"Defines whether requests with encoded hash characters in the path are allowed." json:"allowEncodedHash,omitempty" toml:"allowEncodedHash,omitempty" yaml:"allowEncodedHash,omitempty" export:"true"`
|
||||
}
|
||||
|
||||
func (ec *EncodedCharacters) SetDefaults() {
|
||||
ec.AllowEncodedSlash = true
|
||||
ec.AllowEncodedBackSlash = true
|
||||
ec.AllowEncodedNullCharacter = true
|
||||
ec.AllowEncodedSemicolon = true
|
||||
ec.AllowEncodedPercent = true
|
||||
ec.AllowEncodedQuestionMark = true
|
||||
ec.AllowEncodedHash = true
|
||||
}
|
||||
|
||||
// HTTP2Config is the HTTP2 configuration of an entry point.
|
||||
type HTTP2Config struct {
|
||||
MaxConcurrentStreams int32 `description:"Specifies the number of concurrent streams per connection that each client is allowed to initiate." json:"maxConcurrentStreams,omitempty" toml:"maxConcurrentStreams,omitempty" yaml:"maxConcurrentStreams,omitempty" export:"true"`
|
||||
|
|
|
|||
|
|
@ -167,7 +167,7 @@ func applyModel(cfg dynamic.Configuration) dynamic.Configuration {
|
|||
if m.DeniedEncodedPathCharacters != nil {
|
||||
// As the denied encoded path characters option is not configurable at the router level,
|
||||
// we can simply copy the whole structure to override the router's default config.
|
||||
cp.DeniedEncodedPathCharacters = *m.DeniedEncodedPathCharacters
|
||||
cp.DeniedEncodedPathCharacters = m.DeniedEncodedPathCharacters
|
||||
}
|
||||
|
||||
rtName := name
|
||||
|
|
|
|||
|
|
@ -2,29 +2,10 @@ package router
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/traefik/traefik/v2/pkg/log"
|
||||
)
|
||||
|
||||
// denyFragment rejects the request if the URL path contains a fragment (hash character).
|
||||
// When go receives an HTTP request, it assumes the absence of fragment URL.
|
||||
// However, it is still possible to send a fragment in the request.
|
||||
// In this case, Traefik will encode the '#' character, altering the request's intended meaning.
|
||||
// To avoid this behavior, the following function rejects requests that include a fragment in the URL.
|
||||
func denyFragment(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if strings.Contains(req.URL.RawPath, "#") {
|
||||
log.WithoutContext().Debugf("Rejecting request because it contains a fragment in the URL path: %s", req.URL.RawPath)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
// denyEncodedPathCharacters reject the request if the escaped path contains encoded characters in the given list.
|
||||
func denyEncodedPathCharacters(encodedCharacters map[string]struct{}, h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
|||
|
|
@ -8,42 +8,6 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_denyFragment(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
name: "Rejects fragment character",
|
||||
url: "http://example.com/#",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "Allows without fragment",
|
||||
url: "http://example.com/",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := denyFragment(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, test.url, nil)
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(res, req)
|
||||
|
||||
assert.Equal(t, test.wantStatus, res.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_denyEncodedPathCharacters(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
|
|
|||
|
|
@ -223,14 +223,11 @@ func (m *Manager) buildHTTPHandler(ctx context.Context, router *runtime.RouterIn
|
|||
chain = chain.Append(denyrouterrecursion.WrapHandler(routerName))
|
||||
}
|
||||
|
||||
// Here we are adding deny handlers for encoded path characters and fragment.
|
||||
// Deny handler are only added for root routers, child routers are protected by their parent router deny handlers.
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
return denyFragment(next), nil
|
||||
})
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
return denyEncodedPathCharacters(router.DeniedEncodedPathCharacters.Map(), next), nil
|
||||
})
|
||||
if router.DeniedEncodedPathCharacters != nil {
|
||||
chain = chain.Append(func(next http.Handler) (http.Handler, error) {
|
||||
return denyEncodedPathCharacters(router.DeniedEncodedPathCharacters.Map(), next), nil
|
||||
})
|
||||
}
|
||||
|
||||
return chain.Extend(*mHandler).Append(tHandler).Then(sHandler)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -910,13 +910,16 @@ func TestManager_BuildHandlers_Deny(t *testing.T) {
|
|||
expectedStatusCode int
|
||||
}{
|
||||
{
|
||||
desc: "unallowed request with encoded slash",
|
||||
desc: "disallow request with encoded slash",
|
||||
requestPath: "/foo%2F",
|
||||
routers: map[string]*dynamic.Router{
|
||||
"parent": {
|
||||
EntryPoints: []string{"web"},
|
||||
Rule: "PathPrefix(`/`)",
|
||||
Service: "service",
|
||||
DeniedEncodedPathCharacters: &dynamic.RouterDeniedEncodedPathCharacters{
|
||||
AllowEncodedSlash: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
services: map[string]*dynamic.Service{
|
||||
|
|
@ -936,9 +939,6 @@ func TestManager_BuildHandlers_Deny(t *testing.T) {
|
|||
EntryPoints: []string{"web"},
|
||||
Rule: "PathPrefix(`/`)",
|
||||
Service: "service",
|
||||
DeniedEncodedPathCharacters: dynamic.RouterDeniedEncodedPathCharacters{
|
||||
AllowEncodedSlash: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
services: map[string]*dynamic.Service{
|
||||
|
|
@ -950,25 +950,6 @@ func TestManager_BuildHandlers_Deny(t *testing.T) {
|
|||
},
|
||||
expectedStatusCode: http.StatusBadGateway,
|
||||
},
|
||||
{
|
||||
desc: "unallowed request with fragment",
|
||||
requestPath: "/foo#",
|
||||
routers: map[string]*dynamic.Router{
|
||||
"parent": {
|
||||
EntryPoints: []string{"web"},
|
||||
Rule: "PathPrefix(`/`)",
|
||||
Service: "service",
|
||||
},
|
||||
},
|
||||
services: map[string]*dynamic.Service{
|
||||
"service": {
|
||||
LoadBalancer: &dynamic.ServersLoadBalancer{
|
||||
Servers: []dynamic.Server{{URL: "http://localhost:8080"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
|
|
|
|||
|
|
@ -606,6 +606,8 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
|
|||
|
||||
handler = normalizePath(handler)
|
||||
|
||||
handler = denyFragment(handler)
|
||||
|
||||
serverHTTP := &http.Server{
|
||||
Protocols: &protocols,
|
||||
Handler: handler,
|
||||
|
|
@ -685,6 +687,24 @@ func (t *trackedConnection) Close() error {
|
|||
return t.WriteCloser.Close()
|
||||
}
|
||||
|
||||
// denyFragment rejects the request if the URL path contains a fragment (hash character).
|
||||
// When go receives an HTTP request, it assumes the absence of fragment URL.
|
||||
// However, it is still possible to send a fragment in the request.
|
||||
// In this case, Traefik will encode the '#' character, altering the request's intended meaning.
|
||||
// To avoid this behavior, the following function rejects requests that include a fragment in the URL.
|
||||
func denyFragment(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
if strings.Contains(req.URL.RawPath, "#") {
|
||||
log.WithoutContext().Debugf("Rejecting request because it contains a fragment in the URL path: %s", req.URL.RawPath)
|
||||
rw.WriteHeader(http.StatusBadRequest)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
h.ServeHTTP(rw, req)
|
||||
})
|
||||
}
|
||||
|
||||
// This function is inspired by http.AllowQuerySemicolons.
|
||||
func encodeQuerySemicolons(h http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
|
|
|
|||
|
|
@ -388,6 +388,42 @@ func TestKeepAliveH2c(t *testing.T) {
|
|||
require.Contains(t, err.Error(), "use of closed network connection")
|
||||
}
|
||||
|
||||
func Test_denyFragment(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
url string
|
||||
wantStatus int
|
||||
}{
|
||||
{
|
||||
name: "Rejects fragment character",
|
||||
url: "http://example.com/#",
|
||||
wantStatus: http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
name: "Allows without fragment",
|
||||
url: "http://example.com/",
|
||||
wantStatus: http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
handler := denyFragment(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, test.url, nil)
|
||||
res := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(res, req)
|
||||
|
||||
assert.Equal(t, test.wantStatus, res.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSanitizePath(t *testing.T) {
|
||||
tests := []struct {
|
||||
path string
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue