mirror of
https://github.com/minio/minio.git
synced 2025-02-27 13:29:15 -05:00
Limit POST form fields and file size + Generic Request Size limiter (#2317)
* Use less memory when receiving a file via multipart * Add generic http request maximum size limiter to secure against malicious clients
This commit is contained in:
parent
7850d17f48
commit
dcc3463e48
@ -347,7 +347,7 @@ func (api objectAPIHandlers) PostPolicyBucketHandler(w http.ResponseWriter, r *h
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fileBody, fileName, formValues, err := extractHTTPFormValues(reader)
|
fileBody, fileName, formValues, err := extractPostPolicyFormValues(reader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorIf(err, "Unable to parse form values.")
|
errorIf(err, "Unable to parse form values.")
|
||||||
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path)
|
writeErrorResponse(w, r, ErrMalformedPOSTRequest, r.URL.Path)
|
||||||
|
@ -39,6 +39,27 @@ func registerHandlers(mux *router.Router, handlerFns ...HandlerFunc) http.Handle
|
|||||||
return f
|
return f
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Adds limiting body size middleware
|
||||||
|
|
||||||
|
// Set the body size limit to 6 Gb = Maximum object size + other possible data
|
||||||
|
// in the same request
|
||||||
|
const requestMaxBodySize = 1024 * 1024 * 1024 * (5 + 1)
|
||||||
|
|
||||||
|
type requestSizeLimitHandler struct {
|
||||||
|
handler http.Handler
|
||||||
|
maxBodySize int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func setRequestSizeLimitHandler(h http.Handler) http.Handler {
|
||||||
|
return requestSizeLimitHandler{handler: h, maxBodySize: requestMaxBodySize}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h requestSizeLimitHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// Restricting read data to a given maximum length
|
||||||
|
r.Body = http.MaxBytesReader(w, r.Body, h.maxBodySize)
|
||||||
|
h.handler.ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
// Adds redirect rules for incoming requests.
|
// Adds redirect rules for incoming requests.
|
||||||
type redirectHandler struct {
|
type redirectHandler struct {
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
|
@ -52,6 +52,12 @@ var (
|
|||||||
// Add new variable global values here.
|
// Add new variable global values here.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Limit fields size (except file) to 1Mib since Policy document
|
||||||
|
// can reach that size according to https://aws.amazon.com/articles/1434
|
||||||
|
maxFormFieldSize = int64(1024 * 1024)
|
||||||
|
)
|
||||||
|
|
||||||
// global colors.
|
// global colors.
|
||||||
var (
|
var (
|
||||||
colorBlue = color.New(color.FgBlue).SprintfFunc()
|
colorBlue = color.New(color.FgBlue).SprintfFunc()
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"mime/multipart"
|
"mime/multipart"
|
||||||
@ -99,12 +98,11 @@ func extractMetadataFromHeader(header http.Header) map[string]string {
|
|||||||
return metadata
|
return metadata
|
||||||
}
|
}
|
||||||
|
|
||||||
func extractHTTPFormValues(reader *multipart.Reader) (io.Reader, string, map[string]string, error) {
|
// Extract form fields and file data from a HTTP POST Policy
|
||||||
|
func extractPostPolicyFormValues(reader *multipart.Reader) (filePart io.Reader, fileName string, formValues map[string]string, err error) {
|
||||||
/// HTML Form values
|
/// HTML Form values
|
||||||
formValues := make(map[string]string)
|
formValues = make(map[string]string)
|
||||||
filePart := new(bytes.Buffer)
|
fileName = ""
|
||||||
fileName := ""
|
|
||||||
var err error
|
|
||||||
for err == nil {
|
for err == nil {
|
||||||
var part *multipart.Part
|
var part *multipart.Part
|
||||||
part, err = reader.NextPart()
|
part, err = reader.NextPart()
|
||||||
@ -112,19 +110,22 @@ func extractHTTPFormValues(reader *multipart.Reader) (io.Reader, string, map[str
|
|||||||
canonicalFormName := http.CanonicalHeaderKey(part.FormName())
|
canonicalFormName := http.CanonicalHeaderKey(part.FormName())
|
||||||
if canonicalFormName != "File" {
|
if canonicalFormName != "File" {
|
||||||
var buffer []byte
|
var buffer []byte
|
||||||
buffer, err = ioutil.ReadAll(part)
|
limitReader := io.LimitReader(part, maxFormFieldSize+1)
|
||||||
|
buffer, err = ioutil.ReadAll(limitReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", nil, err
|
return nil, "", nil, err
|
||||||
}
|
}
|
||||||
|
if int64(len(buffer)) > maxFormFieldSize {
|
||||||
|
return nil, "", nil, errSizeUnexpected
|
||||||
|
}
|
||||||
formValues[canonicalFormName] = string(buffer)
|
formValues[canonicalFormName] = string(buffer)
|
||||||
} else {
|
} else {
|
||||||
if _, err = io.Copy(filePart, part); err != nil {
|
filePart = io.LimitReader(part, maxObjectSize)
|
||||||
return nil, "", nil, err
|
|
||||||
}
|
|
||||||
fileName = part.FileName()
|
fileName = part.FileName()
|
||||||
|
// As described in S3 spec, we expect file to be the last form field
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return filePart, fileName, formValues, nil
|
return filePart, fileName, formValues, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -79,6 +79,8 @@ func configureServerHandler(srvCmdConfig serverCmdConfig) http.Handler {
|
|||||||
var handlerFns = []HandlerFunc{
|
var handlerFns = []HandlerFunc{
|
||||||
// Limits the number of concurrent http requests.
|
// Limits the number of concurrent http requests.
|
||||||
setRateLimitHandler,
|
setRateLimitHandler,
|
||||||
|
// Limits all requests size to a maximum fixed limit
|
||||||
|
setRequestSizeLimitHandler,
|
||||||
// Adds 'crossdomain.xml' policy handler to serve legacy flash clients.
|
// Adds 'crossdomain.xml' policy handler to serve legacy flash clients.
|
||||||
setCrossDomainPolicy,
|
setCrossDomainPolicy,
|
||||||
// Redirect some pre-defined browser request paths to a static location prefix.
|
// Redirect some pre-defined browser request paths to a static location prefix.
|
||||||
|
@ -32,3 +32,6 @@ var errInvalidToken = errors.New("Invalid token")
|
|||||||
|
|
||||||
// If x-amz-content-sha256 header value mismatches with what we calculate.
|
// If x-amz-content-sha256 header value mismatches with what we calculate.
|
||||||
var errContentSHA256Mismatch = errors.New("sha256 mismatch")
|
var errContentSHA256Mismatch = errors.New("sha256 mismatch")
|
||||||
|
|
||||||
|
// used when we deal with data larger than expected
|
||||||
|
var errSizeUnexpected = errors.New("data size larger than expected")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user