From ea48dbe128917c9338ea61586c6fdfacd776e6d9 Mon Sep 17 00:00:00 2001 From: Mickael KERJEAN Date: Fri, 8 Mar 2019 17:29:21 +1100 Subject: [PATCH] improve (security): add support for the X-Requested-With header --- client/helpers/ajax.js | 6 +++++- server/main.go | 25 +++++++++++++------------ server/middleware/http.go | 12 +++++++++++- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/client/helpers/ajax.js b/client/helpers/ajax.js index 77153618..c64dd825 100644 --- a/client/helpers/ajax.js +++ b/client/helpers/ajax.js @@ -1,7 +1,9 @@ export function http_get(url, type = 'json'){ return new Promise((done, err) => { var xhr = new XMLHttpRequest(); + xhr.open('GET', url, true); xhr.withCredentials = true; + xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest'); xhr.onreadystatechange = function() { if (xhr.readyState === XMLHttpRequest.DONE) { if(xhr.status === 200){ @@ -24,7 +26,6 @@ export function http_get(url, type = 'json'){ } } } - xhr.open('GET', url, true); xhr.send(null); }); } @@ -34,6 +35,7 @@ export function http_post(url, data, type = 'json'){ var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.withCredentials = true; + xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest'); if(type === 'json'){ data = JSON.stringify(data); xhr.setRequestHeader('Content-Type', 'application/json'); @@ -68,6 +70,7 @@ export function http_delete(url){ var xhr = new XMLHttpRequest(); xhr.open("DELETE", url, true); xhr.withCredentials = true; + xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest'); xhr.onload = function () { if (xhr.readyState === XMLHttpRequest.DONE) { if(xhr.status === 200){ @@ -95,6 +98,7 @@ export function http_options(url){ var xhr = new XMLHttpRequest(); xhr.open("OPTIONS", url, true); xhr.withCredentials = true; + xhr.setRequestHeader('X-Requested-With', 'XmlHttpRequest'); xhr.onload = function(){ if(xhr.readyState === XMLHttpRequest.DONE){ if(xhr.status !== 200){ diff --git a/server/main.go b/server/main.go index b804eed7..e90f7277 100644 --- a/server/main.go +++ b/server/main.go @@ -41,35 +41,36 @@ func Init(a *App) { // API for Session session := r.PathPrefix("/api/session").Subrouter() - middlewares = []Middleware{ ApiHeaders, SecureHeaders, SessionStart } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, SessionStart } session.HandleFunc("", NewMiddlewareChain(SessionGet, middlewares, *a)).Methods("GET") - middlewares = []Middleware{ ApiHeaders, SecureHeaders, BodyParser } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, BodyParser } session.HandleFunc("", NewMiddlewareChain(SessionAuthenticate, middlewares, *a)).Methods("POST") - middlewares = []Middleware{ ApiHeaders, SecureHeaders, SessionTry } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, SessionTry } session.HandleFunc("", NewMiddlewareChain(SessionLogout, middlewares, *a)).Methods("DELETE") - middlewares = []Middleware{ ApiHeaders, SecureHeaders } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax } session.HandleFunc("/auth/{service}", NewMiddlewareChain(SessionOAuthBackend, middlewares, *a)).Methods("GET") // API for admin - middlewares = []Middleware{ ApiHeaders } + middlewares = []Middleware{ ApiHeaders, SecureAjax } admin := r.PathPrefix("/admin/api").Subrouter() admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionGet, middlewares, *a)).Methods("GET") admin.HandleFunc("/session", NewMiddlewareChain(AdminSessionAuthenticate, middlewares, *a)).Methods("POST") - middlewares = []Middleware{ ApiHeaders, AdminOnly } + middlewares = []Middleware{ ApiHeaders, AdminOnly, SecureAjax } admin.HandleFunc("/plugin", NewMiddlewareChain(FetchPluginsHandler, middlewares, *a)).Methods("GET") admin.HandleFunc("/log", NewMiddlewareChain(FetchLogHandler, middlewares, *a)).Methods("GET") admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigHandler, middlewares, *a)).Methods("GET") admin.HandleFunc("/config", NewMiddlewareChain(PrivateConfigUpdateHandler, middlewares, *a)).Methods("POST") - middlewares = []Middleware{ IndexHeaders } + middlewares = []Middleware{ IndexHeaders, SecureAjax } r.PathPrefix("/admin").Handler(http.HandlerFunc(NewMiddlewareChain(IndexHandler(FILE_INDEX), middlewares, *a))).Methods("GET") // API for File management files := r.PathPrefix("/api/files").Subrouter() middlewares = []Middleware{ ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly } - files.HandleFunc("/ls", NewMiddlewareChain(FileLs, middlewares, *a)).Methods("GET") files.HandleFunc("/cat", NewMiddlewareChain(FileCat, middlewares, *a)).Methods("GET", "HEAD") + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly } files.HandleFunc("/cat", NewMiddlewareChain(FileAccess, middlewares, *a)).Methods("OPTIONS") files.HandleFunc("/cat", NewMiddlewareChain(FileSave, middlewares, *a)).Methods("POST") + files.HandleFunc("/ls", NewMiddlewareChain(FileLs, middlewares, *a)).Methods("GET") files.HandleFunc("/mv", NewMiddlewareChain(FileMv, middlewares, *a)).Methods("GET") files.HandleFunc("/rm", NewMiddlewareChain(FileRm, middlewares, *a)).Methods("GET") files.HandleFunc("/mkdir", NewMiddlewareChain(FileMkdir, middlewares, *a)).Methods("GET") @@ -81,13 +82,13 @@ func Init(a *App) { // API for Shared link share := r.PathPrefix("/api/share").Subrouter() - middlewares = []Middleware{ ApiHeaders, SecureHeaders, SessionStart, LoggedInOnly } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, SessionStart, LoggedInOnly } share.HandleFunc("", NewMiddlewareChain(ShareList, middlewares, *a)).Methods("GET") - middlewares = []Middleware{ ApiHeaders, SecureHeaders, BodyParser } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, BodyParser } share.HandleFunc("/{share}/proof", NewMiddlewareChain(ShareVerifyProof, middlewares, *a)).Methods("POST") - middlewares = []Middleware{ ApiHeaders, SecureHeaders, CanManageShare } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, CanManageShare } share.HandleFunc("/{share}", NewMiddlewareChain(ShareDelete, middlewares, *a)).Methods("DELETE") - middlewares = []Middleware{ ApiHeaders, SecureHeaders, BodyParser, CanManageShare } + middlewares = []Middleware{ ApiHeaders, SecureHeaders, SecureAjax, BodyParser, CanManageShare } share.HandleFunc("/{share}", NewMiddlewareChain(ShareUpsert, middlewares, *a)).Methods("POST") // Webdav server / Shared Link diff --git a/server/middleware/http.go b/server/middleware/http.go index 7f8a6f48..09b6499d 100644 --- a/server/middleware/http.go +++ b/server/middleware/http.go @@ -16,6 +16,17 @@ func ApiHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, } } +func SecureAjax(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) { + return func(ctx App, res http.ResponseWriter, req *http.Request) { + if req.Header.Get("X-Requested-With") != "XmlHttpRequest" { + Log.Warning("Intrusion detection: %s - %s", req.RemoteAddr, req.URL.String()) + SendErrorResult(res, ErrNotAllowed) + return + } + fn(ctx, res, req) + } +} + func StaticHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx App, res http.ResponseWriter, req *http.Request) { return func(ctx App, res http.ResponseWriter, req *http.Request) { header := res.Header() @@ -49,7 +60,6 @@ func SecureHeaders(fn func(App, http.ResponseWriter, *http.Request)) func(ctx Ap if Config.Get("general.force_ssl").Bool() { header.Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains; preload") } - header.Set("X-Content-Type-Options", "nosniff") header.Set("X-XSS-Protection", "1; mode=block") header.Set("X-Frame-Options", "DENY")