luci-base: use different cookie names for HTTP and HTTPS
authorJo-Philipp Wich <jo@mein.io>
Fri, 8 Jul 2022 13:38:53 +0000 (15:38 +0200)
committerJo-Philipp Wich <jo@mein.io>
Fri, 8 Jul 2022 13:38:53 +0000 (15:38 +0200)
Since HTTP cookies may not overwrite HTTPS ("secure") ones, users are
frequently unable to log into LuCI when a stale, "secure" `sysauth` cookie
is still present in the browser as it commonly happens after e.g. a
sysupgrade operation or when frequently jumping between HTTP and HTTPS
access.

Rework the dispatcher to set either a `sysauth_http` or `sysauth_https`
cookie, depending on the HTTPS state of the server connection and accept
both cookie names when verifying the session ID.

This allows users to log into a HTTP-only LuCI instance while a stale,
"secure" HTTPS cookie is still present.

Requires commit 2b0539ef9d ("lucihttp: update to latest Git HEAD") to
function properly.

Fixes: #5843
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/luasrc/controller/admin/index.lua
modules/luci-base/luasrc/dispatcher.lua
modules/luci-base/root/usr/share/luci/menu.d/luci-base.json

index 736d0cdccff3cc1362875b8fa01fe8023c05a55e..8f9b481ccef2514500444c104ac2d89ca0fa6962 100644 (file)
@@ -11,9 +11,13 @@ function action_logout()
        if sid then
                utl.ubus("session", "destroy", { ubus_rpc_session = sid })
 
-               luci.http.header("Set-Cookie", "sysauth=%s; expires=%s; path=%s" %{
-                       '', 'Thu, 01 Jan 1970 01:00:00 GMT', dsp.build_url()
-               })
+               local url = dsp.build_url()
+
+               if luci.http.getenv('HTTPS') == 'on' then
+                       luci.http.header("Set-Cookie", "sysauth_https=; expires=Thu, 01 Jan 1970 01:00:00 GMT; path=%s" % url)
+               end
+
+               luci.http.header("Set-Cookie", "sysauth_http=; expires=Thu, 01 Jan 1970 01:00:00 GMT; path=%s" % url)
        end
 
        luci.http.redirect(dsp.build_url())
@@ -185,10 +189,9 @@ end
 
 function action_menu()
        local dsp = require "luci.dispatcher"
-       local utl = require "luci.util"
        local http = require "luci.http"
 
-       local acls = utl.ubus("session", "access", { ubus_rpc_session = http.getcookie("sysauth") })
+       local _, _, acls = dsp.is_authenticated({ methods = { "cookie:sysauth_https", "cookie:sysauth_http" } })
        local menu = dsp.menu_json(acls or {}) or {}
 
        http.prepare_content("application/json")
index d27af0755afd6ea9e7760e048c2c5b66a6aebd27..a3726fb1c11d75ca9a14f9ac6565b2f2b6acfe85 100644 (file)
@@ -343,12 +343,12 @@ local function tree_to_json(node, json)
                                if subnode.sysauth_authenticator == "htmlauth" then
                                        spec.auth = {
                                                login = true,
-                                               methods = { "cookie:sysauth" }
+                                               methods = { "cookie:sysauth_https", "cookie:sysauth_http" }
                                        }
                                elseif subname == "rpc" and subnode.module == "luci.controller.rpc" then
                                        spec.auth = {
                                                login = false,
-                                               methods = { "query:auth", "cookie:sysauth" }
+                                               methods = { "query:auth", "cookie:sysauth_https", "cookie:sysauth_http" }
                                        }
                                elseif subnode.module == "luci.controller.admin.uci" then
                                        spec.auth = {
@@ -732,7 +732,7 @@ local function init_template_engine(ctx)
        return tpl
 end
 
-local function is_authenticated(auth)
+function is_authenticated(auth)
        if type(auth) == "table" and type(auth.methods) == "table" and #auth.methods > 0 then
                local sid, sdat, sacl
                for _, method in ipairs(auth.methods) do
@@ -929,7 +929,8 @@ function dispatch(request)
                                return tpl.render("sysauth", scope)
                        end
 
-                       http.header("Set-Cookie", 'sysauth=%s; path=%s; SameSite=Strict; HttpOnly%s' %{
+                       http.header("Set-Cookie", 'sysauth_%s=%s; path=%s; SameSite=Strict; HttpOnly%s' %{
+                               http.getenv("HTTPS") == "on" and "https" or "http",
                                sid, build_url(), http.getenv("HTTPS") == "on" and "; secure" or ""
                        })
 
index 2a99684c2ce05c15e9e5cba6f2f3086c3d181dc5..605c7ab77723c0215db7e4766274f08a390aa5fb 100644 (file)
@@ -7,7 +7,7 @@
                        "recurse": true
                },
                "auth": {
-                       "methods": [ "cookie:sysauth" ],
+                       "methods": [ "cookie:sysauth_https", "cookie:sysauth_http" ],
                        "login": true
                }
        },
                        "post": true
                },
                "auth": {
-                       "methods": [ "cookie:sysauth" ]
+                       "methods": [ "cookie:sysauth_https", "cookie:sysauth_http" ]
                }
        },
 
                        "post": true
                },
                "auth": {
-                       "methods": [ "cookie:sysauth" ]
+                       "methods": [ "cookie:sysauth_https", "cookie:sysauth_http" ]
                }
        },