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
master
Anis Elleuch 9 years ago committed by Harshavardhana
parent 7850d17f48
commit dcc3463e48
  1. 2
      bucket-handlers.go
  2. 21
      generic-handlers.go
  3. 6
      globals.go
  4. 23
      handler-utils.go
  5. 2
      routers.go
  6. 3
      typed-errors.go

@ -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…
Cancel
Save