mirror of
https://github.com/versity/versitygw.git
synced 2026-09-07 08:36:54 +00:00
Validate required signed headers for both Authorization-header SigV4 requests and presigned URLs. The required signed header set is now `host` plus every incoming header with the `x-amz-` prefix. During request reconstruction, signed headers and explicitly ignored headers are copied into the generated request used for signature verification. If an incoming `x-amz-*` header is present but missing from the client-provided `SignedHeaders`, return `AccessDenied` with a `HeadersNotSigned` field. The `host` header remains part of the canonical request and signed header calculation. Previously, a client could sign a request without an S3 control header and then add that header after signing. For example, a presigned `PUT` URL could be generated with only `host` signed, then the actual request could include an unsigned `X-Amz-Tagging` or `X-Amz-Copy-Source` header. Because the verifier reconstructed the request only from `SignedHeaders`, that extra header was omitted from signature calculation and could pass authentication even though it changed the request semantics. This is now rejected with `AccessDenied`. Expose v4 helper methods for checking required and ignored headers, and update canonical header signing so ignored headers can still be included when a client explicitly lists them in `SignedHeaders`, while `Authorization` remains excluded from signature calculation.
93 lines
2.1 KiB
Go
93 lines
2.1 KiB
Go
package v4
|
|
|
|
import (
|
|
"strings"
|
|
)
|
|
|
|
// Rules houses a set of Rule needed for validation of a
|
|
// string value
|
|
type Rules []Rule
|
|
|
|
// Rule interface allows for more flexible rules and just simply
|
|
// checks whether or not a value adheres to that Rule
|
|
type Rule interface {
|
|
IsValid(value string) bool
|
|
}
|
|
|
|
// IsValid will iterate through all rules and see if any rules
|
|
// apply to the value and supports nested rules
|
|
func (r Rules) IsValid(value string) bool {
|
|
for _, rule := range r {
|
|
if rule.IsValid(value) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// MapRule generic Rule for maps
|
|
type MapRule map[string]struct{}
|
|
|
|
// IsValid for the map Rule satisfies whether it exists in the map
|
|
func (m MapRule) IsValid(value string) bool {
|
|
for key := range m {
|
|
if strings.EqualFold(key, value) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// AllowList is a generic Rule for include listing
|
|
type AllowList struct {
|
|
Rule
|
|
}
|
|
|
|
// IsValid for AllowList checks if the value is within the AllowList
|
|
func (w AllowList) IsValid(value string) bool {
|
|
return w.Rule.IsValid(value)
|
|
}
|
|
|
|
// ExcludeList is a generic Rule for exclude listing
|
|
type ExcludeList struct {
|
|
Rule
|
|
}
|
|
|
|
// IsValid for AllowList checks if the value is within the AllowList
|
|
func (b ExcludeList) IsValid(value string) bool {
|
|
return !b.Rule.IsValid(value)
|
|
}
|
|
|
|
// Patterns is a list of strings to match against
|
|
type Patterns []string
|
|
|
|
// IsValid for Patterns checks each pattern and returns if a match has
|
|
// been found
|
|
func (p Patterns) IsValid(value string) bool {
|
|
for _, pattern := range p {
|
|
if hasPrefixFold(value, pattern) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// InclusiveRules rules allow for rules to depend on one another
|
|
type InclusiveRules []Rule
|
|
|
|
// IsValid will return true if all rules are true
|
|
func (r InclusiveRules) IsValid(value string) bool {
|
|
for _, rule := range r {
|
|
if !rule.IsValid(value) {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// hasPrefixFold tests whether the string s begins with prefix, interpreted as UTF-8 strings,
|
|
// under Unicode case-folding.
|
|
func hasPrefixFold(s, prefix string) bool {
|
|
return len(s) >= len(prefix) && strings.EqualFold(s[0:len(prefix)], prefix)
|
|
}
|