From 142359ce11c4a1e2c4dae2ff7a645c37730b1bb8 Mon Sep 17 00:00:00 2001 From: Azareal Date: Mon, 4 Nov 2019 21:55:52 +1000 Subject: [PATCH] Add SslSchema config setting. Reject URL usernames on registration. Reduce length of char variable name. --- common/email.go | 4 ++-- common/parser.go | 2 +- common/routes_common.go | 2 +- common/site.go | 14 +++++++++----- gen_router.go | 8 ++++---- langs/english.json | 1 + parser_test.go | 14 +++++++------- router_gen/main.go | 8 ++++---- routes/account.go | 11 +++++++---- routes/api.go | 34 +++++++++++++++++----------------- routes/attachments.go | 7 ++++--- routes/common.go | 2 +- routes/panel/common.go | 2 +- 13 files changed, 59 insertions(+), 50 deletions(-) diff --git a/common/email.go b/common/email.go index 833617ae..eccf5360 100644 --- a/common/email.go +++ b/common/email.go @@ -12,7 +12,7 @@ import ( func SendActivationEmail(username string, email string, token string) error { schema := "http" - if Site.EnableSsl { + if Config.SslSchema { schema += "s" } // TODO: Move these to the phrase system @@ -23,7 +23,7 @@ func SendActivationEmail(username string, email string, token string) error { func SendValidationEmail(username string, email string, token string) error { schema := "http" - if Site.EnableSsl { + if Config.SslSchema { schema += "s" } r := func(body *string) func(name, val string) { diff --git a/common/parser.go b/common/parser.go index ee8eda4c..6ebb2059 100644 --- a/common/parser.go +++ b/common/parser.go @@ -872,7 +872,7 @@ func parseMediaString(data string) (media MediaEmbed, ok bool) { host = strings.Split(Site.URL, ":")[0] // ?- Test this as I'm not sure it'll do what it should. If someone's running SSL on port 80 or non-SSL on port 443 then... Well... They're in far worse trouble than this... port = Site.Port - if Site.EnableSsl { + if Config.SslSchema { scheme = "https" } } diff --git a/common/routes_common.go b/common/routes_common.go index 5e65dbe3..516c6552 100644 --- a/common/routes_common.go +++ b/common/routes_common.go @@ -293,7 +293,7 @@ func preRoute(w http.ResponseWriter, r *http.Request) (User, bool) { // TODO: Add a config setting to disable this header // TODO: Have this header cover more things - if Site.EnableSsl { + if Config.SslSchema { w.Header().Set("Content-Security-Policy", "upgrade-insecure-requests") } diff --git a/common/site.go b/common/site.go index 0e07bb5d..0f3e1888 100644 --- a/common/site.go +++ b/common/site.go @@ -62,7 +62,7 @@ type config struct { SslPrivkey string SslFullchain string HashAlgo string // Defaults to bcrypt, and in the future, possibly something stronger - ConvoKey string + ConvoKey string MaxRequestSizeStr string MaxRequestSize int @@ -89,9 +89,9 @@ type config struct { MinifyTemplates bool BuildSlugs bool // TODO: Make this a setting? - PrimaryServer bool - ServerCount int - PostIPCutoff int + PrimaryServer bool + ServerCount int + PostIPCutoff int LogPruneCutoff int DisableLiveTopicList bool @@ -99,13 +99,14 @@ type config struct { //LooseCSP bool LooseHost bool LoosePort bool + SslSchema bool // Pretend we're using SSL, might be useful if a reverse-proxy terminates SSL in-front of Gosora DisableServerPush bool EnableCDNPush bool DisableNoavatarRange bool DisableDefaultNoavatar bool RefNoTrack bool - RefNoRef bool + RefNoRef bool Noavatar string // ? - Move this into the settings table? ItemsPerPage int // ? - Move this into the settings table? @@ -174,6 +175,9 @@ func ProcessConfig() (err error) { Site.URL = strings.TrimSuffix(Site.URL, ":") Site.URL = Site.URL + ":" + Site.Port } + if Site.EnableSsl { + Config.SslSchema = Site.EnableSsl + } if Config.DefaultPath == "" { Config.DefaultPath = "/topics/" } diff --git a/gen_router.go b/gen_router.go index a9a1318e..b18043f5 100644 --- a/gen_router.go +++ b/gen_router.go @@ -808,7 +808,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO: Abstract the redirect logic? w.Header().Set("Connection", "close") var s string - if c.Site.EnableSsl { + if c.Config.SslSchema { s = "s" } var p string @@ -833,8 +833,8 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // TODO: Cover more suspicious strings and at a lower layer than this - for _, char := range req.URL.Path { - if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) { + for _, ch := range req.URL.Path { //char + if ch != '&' && !(ch > 44 && ch < 58) && ch != '=' && ch != '?' && !(ch > 64 && ch < 91) && ch != '\\' && ch != '_' && !(ch > 96 && ch < 123) { r.SuspiciousRequest(req,"Bad char in path") break } @@ -869,7 +869,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { h.Set("X-Frame-Options", "deny") h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing h.Set("X-Content-Type-Options", "nosniff") - if c.Config.RefNoRef || !c.Site.EnableSsl { + if c.Config.RefNoRef || !c.Config.SslSchema { h.Set("Referrer-Policy","no-referrer") } else { h.Set("Referrer-Policy","strict-origin") diff --git a/langs/english.json b/langs/english.json index 43e5d6f7..beb57cb2 100644 --- a/langs/english.json +++ b/langs/english.json @@ -97,6 +97,7 @@ "register_need_username":"You didn't put in a username.", "register_need_email":"You didn't put in an email.", "register_first_word_numeric":"The first word of your name must not be purely numeric", + "register_url_username":"You cannot have a URL within your username", "register_suspicious_email":"Your email address is suspicious.", "register_password_mismatch":"The two passwords don't match.", "register_username_unavailable":"This username isn't available. Try another.", diff --git a/parser_test.go b/parser_test.go index 9015f4fe..4f9e708d 100644 --- a/parser_test.go +++ b/parser_test.go @@ -202,12 +202,12 @@ func TestParser(t *testing.T) { l.Add("//"+url+"\n//"+url, eurl+"
"+eurl) l.Add("//"+url+"\n\n//"+url, eurl+"

"+eurl) - pre2 := c.Site.EnableSsl - c.Site.EnableSsl = true + pre2 := c.Config.SslSchema + c.Config.SslSchema = true local := func(u string) { s := "//" + c.Site.URL fs := "http://" + c.Site.URL - if c.Site.EnableSsl { + if c.Config.SslSchema { s = "https:" + s fs = "https://" + c.Site.URL } @@ -301,13 +301,13 @@ func TestParser(t *testing.T) { break } } - c.Site.EnableSsl = pre2 + c.Config.SslSchema = pre2 l = &METriList{nil} pre := c.Site.URL // Just in case this is localhost... - pre2 = c.Site.EnableSsl + pre2 = c.Config.SslSchema c.Site.URL = "example.com" - c.Site.EnableSsl = true + c.Config.SslSchema = true l.Add("//"+c.Site.URL, ""+c.Site.URL+"") l.Add("//"+c.Site.URL+"\n", ""+c.Site.URL+"
") l.Add("//"+c.Site.URL+"\n//"+c.Site.URL, ""+c.Site.URL+"
"+c.Site.URL+"") @@ -323,7 +323,7 @@ func TestParser(t *testing.T) { } } c.Site.URL = pre - c.Site.EnableSsl = pre2 + c.Config.SslSchema = pre2 c.AddHashLinkType("nnid-", func(sb *strings.Builder, msg string, i *int) { tid, intLen := c.CoerceIntString(msg[*i:]) diff --git a/router_gen/main.go b/router_gen/main.go index a9f96fb6..f1d1b5af 100644 --- a/router_gen/main.go +++ b/router_gen/main.go @@ -531,7 +531,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { // TODO: Abstract the redirect logic? w.Header().Set("Connection", "close") var s string - if c.Site.EnableSsl { + if c.Config.SslSchema { s = "s" } var p string @@ -556,8 +556,8 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { } // TODO: Cover more suspicious strings and at a lower layer than this - for _, char := range req.URL.Path { - if char != '&' && !(char > 44 && char < 58) && char != '=' && char != '?' && !(char > 64 && char < 91) && char != '\\' && char != '_' && !(char > 96 && char < 123) { + for _, ch := range req.URL.Path { //char + if ch != '&' && !(ch > 44 && ch < 58) && ch != '=' && ch != '?' && !(ch > 64 && ch < 91) && ch != '\\' && ch != '_' && !(ch > 96 && ch < 123) { r.SuspiciousRequest(req,"Bad char in path") break } @@ -592,7 +592,7 @@ func (r *GenRouter) ServeHTTP(w http.ResponseWriter, req *http.Request) { h.Set("X-Frame-Options", "deny") h.Set("X-XSS-Protection", "1; mode=block") // TODO: Remove when we add a CSP? CSP's are horrendously glitchy things, tread with caution before removing h.Set("X-Content-Type-Options", "nosniff") - if c.Config.RefNoRef || !c.Site.EnableSsl { + if c.Config.RefNoRef || !c.Config.SslSchema { h.Set("Referrer-Policy","no-referrer") } else { h.Set("Referrer-Policy","strict-origin") diff --git a/routes/account.go b/routes/account.go index 69cf4067..8772e087 100644 --- a/routes/account.go +++ b/routes/account.go @@ -235,6 +235,9 @@ func AccountRegisterSubmit(w http.ResponseWriter, r *http.Request, user c.User) if isNumeric(nameBits[0]) { regError(p.GetErrorPhrase("register_first_word_numeric"), "numeric-name") } + if strings.Contains(name,"http://") || strings.Contains(name,"https://") || strings.Contains(name,"ftp://") || strings.Contains(name,"ssh://") { + regError(p.GetErrorPhrase("register_url_username"), "url-name") + } // TODO: Add a dedicated function for validating emails email := c.SanitiseSingleLine(r.PostFormValue("email")) @@ -807,12 +810,12 @@ func AccountPasswordResetSubmit(w http.ResponseWriter, r *http.Request, user c.U return c.InternalError(err, w, r) } - var schema string - if c.Site.EnableSsl { - schema = "s" + var s string + if c.Config.SslSchema { + s = "s" } - err = c.SendEmail(tuser.Email, p.GetTmplPhrase("password_reset_subject"), p.GetTmplPhrasef("password_reset_body", tuser.Name, "http"+schema+"://"+c.Site.URL+"/accounts/password-reset/token/?uid="+strconv.Itoa(tuser.ID)+"&token="+token)) + err = c.SendEmail(tuser.Email, p.GetTmplPhrase("password_reset_subject"), p.GetTmplPhrasef("password_reset_body", tuser.Name, "http"+s+"://"+c.Site.URL+"/accounts/password-reset/token/?uid="+strconv.Itoa(tuser.ID)+"&token="+token)) if err != nil { return c.LocalError(p.GetErrorPhrase("password_reset_email_fail"), w, r, user) } diff --git a/routes/api.go b/routes/api.go index cdadd047..c6718674 100644 --- a/routes/api.go +++ b/routes/api.go @@ -33,13 +33,13 @@ func writeXMLHeader(w http.ResponseWriter, r *http.Request) { // TODO: Keep track of when a sitemap was last modifed and add a lastmod element for it func SitemapXml(w http.ResponseWriter, r *http.Request) c.RouteError { - var sslBit string - if c.Site.EnableSsl { - sslBit = "s" + var s string + if c.Config.SslSchema { + s = "s" } sitemapItem := func(path string) { w.Write([]byte(` - http` + sslBit + `://` + c.Site.URL + "/" + path + ` + http` + s + `://` + c.Site.URL + "/" + path + ` `)) } @@ -95,13 +95,13 @@ func sitemapSwitch(w http.ResponseWriter, r *http.Request) c.RouteError { } func SitemapForums(w http.ResponseWriter, r *http.Request) c.RouteError { - var sslBit string - if c.Site.EnableSsl { - sslBit = "s" + var s string + if c.Config.SslSchema { + s = "s" } sitemapItem := func(path string) { w.Write([]byte(` - http` + sslBit + `://` + c.Site.URL + path + ` + http` + s + `://` + c.Site.URL + path + ` `)) } @@ -129,13 +129,13 @@ func SitemapForums(w http.ResponseWriter, r *http.Request) c.RouteError { // TODO: Add a global ratelimit. 10 50MB files (smaller if compressed better) per minute? // ? We might have problems with banned users, if they have fewer ViewTopic permissions than guests as they'll be able to see this list. Then again, a banned user could just logout to see it func SitemapTopics(w http.ResponseWriter, r *http.Request) c.RouteError { - var sslBit string - if c.Site.EnableSsl { - sslBit = "s" + var s string + if c.Config.SslSchema { + s = "s" } sitemapItem := func(path string) { w.Write([]byte(` - http` + sslBit + `://` + c.Site.URL + "/" + path + ` + http` + s + `://` + c.Site.URL + "/" + path + ` `)) } @@ -171,13 +171,13 @@ func SitemapTopics(w http.ResponseWriter, r *http.Request) c.RouteError { } func SitemapTopic(w http.ResponseWriter, r *http.Request, page int) c.RouteError { - /*var sslBit string - if c.Site.EnableSsl { - sslBit = "s" + /*var s string + if c.Config.SslSchema { + s = "s" } var sitemapItem = func(path string) { w.Write([]byte(` - http` + sslBit + `://` + c.Site.URL + "/" + path + ` + http` + s + `://` + c.Site.URL + "/" + path + ` `)) }*/ @@ -254,7 +254,7 @@ func APIMe(w http.ResponseWriter, r *http.Request, user c.User) c.RouteError { func OpenSearchXml(w http.ResponseWriter, r *http.Request) c.RouteError { furl := "http" - if c.Site.EnableSsl { + if c.Config.SslSchema { furl += "s" } furl += "://" + c.Site.URL diff --git a/routes/attachments.go b/routes/attachments.go index 0845ad01..f0b971e9 100644 --- a/routes/attachments.go +++ b/routes/attachments.go @@ -61,7 +61,7 @@ func ShowAttachment(w http.ResponseWriter, r *http.Request, user c.User, filenam } else { return c.LocalError("Unknown section", w, r, user) } - + if originTable != "topics" && originTable != "replies" { return c.LocalError("Unknown origin", w, r, user) } @@ -74,10 +74,11 @@ func ShowAttachment(w http.ResponseWriter, r *http.Request, user c.User, filenam if ferr != nil { return ferr } + h := w.Header() if guest.Perms.ViewTopic { - w.Header().Set("Cache-Control", "max-age="+strconv.Itoa(int(c.Year))) + h.Set("Cache-Control", "max-age="+strconv.Itoa(int(c.Year))) } else { - w.Header().Set("Cache-Control", "private") + h.Set("Cache-Control", "private") } } diff --git a/routes/common.go b/routes/common.go index 4c7915cd..7b520a1e 100644 --- a/routes/common.go +++ b/routes/common.go @@ -99,7 +99,7 @@ func renderTemplate2(tmplName string, hookName string, w http.ResponseWriter, r func FootHeaders(w http.ResponseWriter, header *c.Header) { if !header.LooseCSP { - if c.Site.EnableSsl { + if c.Config.SslSchema { w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self' www.youtube-nocookie.com;upgrade-insecure-requests") } else { w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self' www.youtube-nocookie.com") diff --git a/routes/panel/common.go b/routes/panel/common.go index d1b98c92..65133d89 100644 --- a/routes/panel/common.go +++ b/routes/panel/common.go @@ -24,7 +24,7 @@ func successRedirect(dest string, w http.ResponseWriter, r *http.Request, js boo // TODO: Prerender needs to handle dyntmpl templates better... func renderTemplate(tmplName string, w http.ResponseWriter, r *http.Request, header *c.Header, pi interface{}) c.RouteError { if !header.LooseCSP { - if c.Site.EnableSsl { + if c.Config.SslSchema { w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self';upgrade-insecure-requests") } else { w.Header().Set("Content-Security-Policy", "default-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-eval' 'unsafe-inline'; img-src * data: 'unsafe-eval' 'unsafe-inline'; connect-src * 'unsafe-eval' 'unsafe-inline'; frame-src 'self'")