mirror of
https://github.com/versity/versitygw.git
synced 2026-07-02 16:54:25 +00:00
577470214d
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.
62 lines
1.8 KiB
Go
62 lines
1.8 KiB
Go
// Copyright 2026 Versity Software
|
|
// This file is licensed under the Apache License, Version 2.0
|
|
// (the "License"); you may not use this file except in compliance
|
|
// with the License. You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing,
|
|
// software distributed under the License is distributed on an
|
|
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
// KIND, either express or implied. See the License for the
|
|
// specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package s3err
|
|
|
|
import (
|
|
"encoding/xml"
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
// HeadersNotSignedError is returned when signature verification receives
|
|
// required signed headers that were omitted from SignedHeaders.
|
|
type HeadersNotSignedError struct {
|
|
APIError
|
|
HeadersNotSigned string
|
|
}
|
|
|
|
func (e HeadersNotSignedError) XMLBody(requestID, hostID string) []byte {
|
|
return encodeResponse(struct {
|
|
XMLName xml.Name `xml:"Error"`
|
|
Code string
|
|
Message string
|
|
HeadersNotSigned string `xml:",omitempty"`
|
|
RequestID string `xml:"RequestId,omitempty"`
|
|
HostID string `xml:"HostId,omitempty"`
|
|
}{
|
|
Code: e.Code,
|
|
Message: e.Description,
|
|
HeadersNotSigned: e.HeadersNotSigned,
|
|
RequestID: requestID,
|
|
HostID: hostID,
|
|
})
|
|
}
|
|
|
|
func (e HeadersNotSignedError) Is(target error) bool {
|
|
t, ok := target.(APIError)
|
|
return ok && e.APIError == t
|
|
}
|
|
|
|
func GetHeadersNotSignedErr(headers []string) HeadersNotSignedError {
|
|
return HeadersNotSignedError{
|
|
APIError: APIError{
|
|
Code: "AccessDenied",
|
|
Description: "There were headers present in the request which were not signed",
|
|
HTTPStatusCode: http.StatusForbidden,
|
|
},
|
|
HeadersNotSigned: strings.Join(headers, ", "),
|
|
}
|
|
}
|