From 4b94b27a063ea80b8ac84dd35ddee65dbb49c7a0 Mon Sep 17 00:00:00 2001 From: Matija Obreza Date: Sun, 20 Jan 2019 21:50:29 +0100 Subject: [PATCH] Add check for referrer in req.Referer - typo as per standard --- README.md | 10 ++++++++++ cli/help/help.go | 8 +++++++- cli/server/server.go | 5 +++++ config/config.go | 12 ++++++++++++ handle/handle.go | 44 +++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 77 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 317c9dc..47f0336 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,13 @@ URL_PREFIX= # served using HTTP. TLS_CERT= TLS_KEY= +# List of accepted HTTP referrers. Return 403 if HTTP header `Referer` does not +# match prefixes provided in the list. +# Examples: +# 'REFERRERS=http://localhost,https://...,https://another.name' +# To accept missing referrer header, add a blank entry (start comma): +# 'REFERRERS=,http://localhost,https://another.name' +REFERRERS= ``` ### YAML Configuration File @@ -53,6 +60,9 @@ folder: /web url-prefix: "" tls-cert: "" tls-key: "" +referrers: + - http://localhost + - https://my.site ``` ## Deployment diff --git a/cli/help/help.go b/cli/help/help.go index 3d6504f..c718fb4 100644 --- a/cli/help/help.go +++ b/cli/help/help.go @@ -63,6 +63,10 @@ ENVIRONMENT VARIABLES The prefix to use in the URL path. If supplied, then the prefix must start with a forward-slash and NOT end with a forward-slash. If not supplied then no prefix is used. + REFERRERS + A comma-separated list of valid HTTP Referer prefixes. If incoming header + value is not in this list, a 403 HTTP error is returned. + Examples: 'http://localhost,https://some.site,http://other.site:8080' CONFIGURATION FILE Configuration can also managed used a YAML configuration file. To select the @@ -81,6 +85,8 @@ CONFIGURATION FILE tls-cert: "" tls-key: "" url-prefix: "" + referrers: + - http://localhost ---------------------------------------------------------------------------- USAGE @@ -99,7 +105,7 @@ USAGE export PORT=80 static-file-server Retrieve with: wget http://my.machine/sub/my.file - + export FOLDER=/var/www static-file-server -c config.yml Result: Runs with values from config.yml, but with the folder being diff --git a/cli/server/server.go b/cli/server/server.go index 0d12604..893ab41 100644 --- a/cli/server/server.go +++ b/cli/server/server.go @@ -34,11 +34,16 @@ func Run() error { // configuration. func handlerSelector() (handler http.HandlerFunc) { var serveFileHandler handle.FileServerFunc + serveFileHandler = http.ServeFile if config.Get.Debug { serveFileHandler = handle.WithLogging(serveFileHandler) } + if config.Get.Referrers != nil { + serveFileHandler = handle.WithReferrers(serveFileHandler, config.Get.Referrers) + } + // Choose and set the appropriate, optimized static file serving function. if 0 == len(config.Get.URLPrefix) { handler = handle.Basic(serveFileHandler, config.Get.Folder) diff --git a/config/config.go b/config/config.go index 0a6eea7..2b947fe 100644 --- a/config/config.go +++ b/config/config.go @@ -22,6 +22,7 @@ var ( TLSCert string `yaml:"tls-cert"` TLSKey string `yaml:"tls-key"` URLPrefix string `yaml:"url-prefix"` + Referrers []string `yaml:"referrers"` } ) @@ -34,6 +35,7 @@ const ( tlsCertKey = "TLS_CERT" tlsKeyKey = "TLS_KEY" urlPrefixKey = "URL_PREFIX" + referrersKey = "REFERRERS" ) const ( @@ -61,6 +63,7 @@ func setDefaults() { Get.TLSCert = defaultTLSCert Get.TLSKey = defaultTLSKey Get.URLPrefix = defaultURLPrefix + Get.Referrers = nil } // Load the configuration file. @@ -108,6 +111,7 @@ func overrideWithEnvVars() { Get.TLSCert = envAsStr(tlsCertKey, Get.TLSCert) Get.TLSKey = envAsStr(tlsKeyKey, Get.TLSKey) Get.URLPrefix = envAsStr(urlPrefixKey, Get.URLPrefix) + Get.Referrers = strAsArray(envAsStr(referrersKey, "")) } // validate the configuration. @@ -211,3 +215,11 @@ func strAsBool(value string) (result bool, err error) { } return } + +func strAsArray(value string) (result []string) { + if (value == "") { + return nil + } + result = strings.Split(value, ","); + return +} \ No newline at end of file diff --git a/handle/handle.go b/handle/handle.go index 17ca3bc..8f5cc38 100644 --- a/handle/handle.go +++ b/handle/handle.go @@ -26,12 +26,54 @@ type ListenerFunc func(string, http.HandlerFunc) error // requesting client. type FileServerFunc func(http.ResponseWriter, *http.Request, string) +func validReferrer(s []string, e string) bool { + if (s == nil) { + // log.Printf("No referrers specified, all fine.") + return true + } + + // log.Printf("Checking referrers " + strings.Join(s, ",") + " against " + e) + + for _, a := range s { + // Handle blank HTTP Referer header, if configured + if (a == "") { + if (e == "") { + // log.Printf("No referrer in request. Allowing."); + return true; + } + // Continue loop (all strings start with "") + continue; + } + + // Compare header with allowed prefixes + if strings.HasPrefix(e, a) { + // log.Printf(strings.Join([]string{ "Referrer match", e, a }, " ")); + return true + } + } + return false +} + +func WithReferrers(serveFile FileServerFunc, referrers []string) FileServerFunc { + return func(w http.ResponseWriter, r *http.Request, name string) { + if (validReferrer(referrers, r.Referer())) { + // log.Printf("Serving file.") + serveFile(w, r, name) + } else { + // log.Printf(strings.Join([]string{"Invalid referrer", r.Referer(), "Not in", strings.Join(referrers, ",")}, " ")) + http.Error(w, strings.Join([]string{ "Invalid source", r.Referer() }, " "), 403) + return + } + } +} + // WithLogging returns a function that logs information about the request prior // to serving the requested file. func WithLogging(serveFile FileServerFunc) FileServerFunc { return func(w http.ResponseWriter, r *http.Request, name string) { log.Printf( - "REQ: %s %s %s%s -> %s\n", + "REQ from %s: %s %s %s%s -> %s\n", + r.Referer(), r.Method, r.Proto, r.Host,