From 04ef581c02bbe3cec8784a156798e9556121a14f Mon Sep 17 00:00:00 2001 From: "Gerasimos (Makis) Maropoulos" Date: Thu, 21 Jul 2022 13:28:44 +0300 Subject: [PATCH] fix CVE-2020-5398 reported through security issue report by @motoyasu-saburi --- context/context.go | 26 +++++++++++++++++++++++++- core/router/fs.go | 2 +- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/context/context.go b/context/context.go index 64e7b7e87..2d99da2d8 100644 --- a/context/context.go +++ b/context/context.go @@ -23,6 +23,7 @@ import ( "strings" "sync/atomic" "time" + "unicode" "unsafe" "github.com/kataras/iris/v12/core/memstore" @@ -5067,10 +5068,33 @@ func (ctx *Context) SendFileWithRate(src, destName string, limit float64, burst destName = filepath.Base(src) } - ctx.writer.Header().Set(ContentDispositionHeaderKey, "attachment;filename="+destName) + ctx.writer.Header().Set(ContentDispositionHeaderKey, MakeDisposition(destName)) return ctx.ServeFileWithRate(src, limit, burst) } +// MakeDisposition generates an HTTP Content-Disposition field-value. +// Similar solution followed by: Spring(Java), Symfony(PHP) and Ruby on Rails frameworks too. +// +// Fixes CVE-2020-5398. Reported by motoyasu-saburi. +func MakeDisposition(filename string) string { + if isASCII(filename) { + return `attachment; filename="` + filename + `"` + } + + return `attachment; filename*=UTF-8''` + url.QueryEscape(filename) +} + +// Found at: https://stackoverflow.com/questions/53069040/checking-a-string-contains-only-ascii-characters +// A faster (better, more idiomatic) version, which avoids unnecessary rune conversions. +func isASCII(s string) bool { + for i := 0; i < len(s); i++ { + if s[i] > unicode.MaxASCII { + return false + } + } + return true +} + // +------------------------------------------------------------+ // | Cookies | // +------------------------------------------------------------+ diff --git a/core/router/fs.go b/core/router/fs.go index b83539d7c..0864bedc7 100644 --- a/core/router/fs.go +++ b/core/router/fs.go @@ -321,7 +321,7 @@ func FileServer(fs http.FileSystem, options DirOptions) context.Handler { destName = nameFunc(destName) } - ctx.ResponseWriter().Header().Set(context.ContentDispositionHeaderKey, "attachment;filename="+destName) + ctx.ResponseWriter().Header().Set(context.ContentDispositionHeaderKey, context.MakeDisposition(destName)) } // the encoding saved from the negotiation.