Merge pull request #2383 from freifunk-gluon/web-fixes
gluon-web: prohibit cross-origin POST requests
This commit is contained in:
commit
68e8d32570
4
.github/workflows/lint.yml
vendored
4
.github/workflows/lint.yml
vendored
@ -10,7 +10,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: sudo apt install lua-check
|
||||
run: sudo apt-get -y update && sudo apt-get -y install lua-check
|
||||
- name: Install example site
|
||||
run: ln -s ./docs/site-example ./site
|
||||
- name: Lint Lua code
|
||||
@ -22,7 +22,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: sudo apt install shellcheck
|
||||
run: sudo apt-get -y update && sudo apt-get -y install shellcheck
|
||||
- name: Install example site
|
||||
run: ln -s ./docs/site-example ./site
|
||||
- name: Lint shell code
|
||||
|
@ -44,7 +44,6 @@ $Id$
|
||||
|
||||
<div class="gluon-page-actions">
|
||||
<input type="hidden" name="step" value="2" />
|
||||
<input type="hidden" name="token" value="<%=token%>" />
|
||||
<input class="gluon-button gluon-button-submit" type="submit" value="<%:Upload image%>" />
|
||||
</div>
|
||||
</form>
|
||||
|
@ -49,13 +49,11 @@ You may obtain a copy of the License at
|
||||
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" style="display:inline">
|
||||
<input type="hidden" name="step" value="3" />
|
||||
<input type="hidden" name="keepcfg" value="<%=keepconfig and "1" or "0"%>" />
|
||||
<input type="hidden" name="token" value="<%=token%>" />
|
||||
<input class="gluon-button gluon-button-submit" type="submit" value="<%:Continue%>" />
|
||||
</form>
|
||||
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" style="display:inline">
|
||||
<input type="hidden" name="step" value="1" />
|
||||
<input type="hidden" name="keepcfg" value="<%=keepconfig and "1" or "0"%>" />
|
||||
<input type="hidden" name="token" value="<%=token%>" />
|
||||
<input class="gluon-button gluon-button-reset" type="submit" value="<%:Cancel%>" />
|
||||
</form>
|
||||
</div>
|
||||
|
@ -1,5 +1,4 @@
|
||||
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" data-update="reset">
|
||||
<input type="hidden" name="token" value="<%=token%>" />
|
||||
<input type="hidden" name="<%=id%>" value="1" />
|
||||
|
||||
<div class="gluon-form" id="form-<%=id%>">
|
||||
|
@ -184,9 +184,15 @@ local function dispatch(config, http, request)
|
||||
return
|
||||
end
|
||||
|
||||
http:parse_input(node.filehandler)
|
||||
local ok, err = pcall(http.parse_input, http, node.filehandler)
|
||||
if not ok then
|
||||
http:status(400, "Bad request")
|
||||
http:prepare_content("text/plain")
|
||||
http:write(err .. "\r\n")
|
||||
return
|
||||
end
|
||||
|
||||
local ok, err = pcall(node.target)
|
||||
ok, err = pcall(node.target)
|
||||
if not ok then
|
||||
http:status(500, "Internal Server Error")
|
||||
renderer.render_layout("error/500", {
|
||||
@ -208,6 +214,6 @@ return function(config, http)
|
||||
if not ok then
|
||||
http:status(500, "Internal Server Error")
|
||||
http:prepare_content("text/plain")
|
||||
http:write(err)
|
||||
http:write(err .. "\r\n")
|
||||
end
|
||||
end
|
||||
|
@ -108,16 +108,11 @@ end
|
||||
-- o String value containing a chunk of the file data
|
||||
-- o Boolean which indicates whether the current chunk is the last one (eof)
|
||||
local function mimedecode_message_body(src, msg, filecb)
|
||||
|
||||
if msg and msg.env.CONTENT_TYPE then
|
||||
msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
|
||||
local mime_boundary = (msg.env.CONTENT_TYPE or ''):match("^multipart/form%-data; boundary=(.+)$")
|
||||
if not mime_boundary then
|
||||
error("Invalid Content-Type found")
|
||||
end
|
||||
|
||||
if not msg.mime_boundary then
|
||||
return nil, "Invalid Content-Type found"
|
||||
end
|
||||
|
||||
|
||||
local tlen = 0
|
||||
local inhdr = false
|
||||
local field = nil
|
||||
@ -188,10 +183,10 @@ local function mimedecode_message_body(src, msg, filecb)
|
||||
local spos, epos, found
|
||||
|
||||
repeat
|
||||
spos, epos = data:find("\r\n--" .. msg.mime_boundary .. "\r\n", 1, true)
|
||||
spos, epos = data:find("\r\n--" .. mime_boundary .. "\r\n", 1, true)
|
||||
|
||||
if not spos then
|
||||
spos, epos = data:find("\r\n--" .. msg.mime_boundary .. "--\r\n", 1, true)
|
||||
spos, epos = data:find("\r\n--" .. mime_boundary .. "--\r\n", 1, true)
|
||||
end
|
||||
|
||||
|
||||
@ -250,20 +245,61 @@ local function mimedecode_message_body(src, msg, filecb)
|
||||
return true
|
||||
end
|
||||
|
||||
return pump(src, snk)
|
||||
assert(pump(src, snk))
|
||||
end
|
||||
|
||||
local function check_post_origin(msg)
|
||||
local default_port = '80'
|
||||
local request_scheme = 'http'
|
||||
if msg.env.HTTPS then
|
||||
default_port = '443'
|
||||
request_scheme = 'https'
|
||||
end
|
||||
|
||||
local request_host = msg.env.HTTP_HOST
|
||||
if not request_host then
|
||||
error('POST request without Host header')
|
||||
end
|
||||
if not request_host:match(':[0-9]+$') then
|
||||
request_host = request_host .. ':' .. default_port
|
||||
end
|
||||
|
||||
local origin = msg.env.HTTP_ORIGIN
|
||||
if not origin then
|
||||
error('POST request without Origin header')
|
||||
end
|
||||
local origin_scheme, origin_host = origin:match('^([^:]*)://(.*)$')
|
||||
if not origin_host then
|
||||
error('POST request with invalid Origin header')
|
||||
end
|
||||
if not origin_host:match(':[0-9]+$') then
|
||||
local origin_port
|
||||
if origin_scheme == 'http' then
|
||||
origin_port = '80'
|
||||
elseif origin_scheme == 'https' then
|
||||
origin_port = '443'
|
||||
else
|
||||
error('POST request with invalid Origin header')
|
||||
end
|
||||
origin_host = origin_host .. ':' .. origin_port
|
||||
end
|
||||
|
||||
if request_scheme ~= origin_scheme or request_host ~= origin_host then
|
||||
error('Invalid cross-origin POST')
|
||||
end
|
||||
end
|
||||
|
||||
-- This function will examine the Content-Type within the given message object
|
||||
-- to select the appropriate content decoder.
|
||||
-- Currently only the multipart/form-data mime type is supported.
|
||||
function M.parse_message_body(src, msg, filecb)
|
||||
if not (msg.env.REQUEST_METHOD == "POST" and msg.env.CONTENT_TYPE) then
|
||||
if msg.env.REQUEST_METHOD ~= "POST" then
|
||||
return
|
||||
end
|
||||
|
||||
if msg.env.CONTENT_TYPE:match("^multipart/form%-data") then
|
||||
return mimedecode_message_body(src, msg, filecb)
|
||||
end
|
||||
check_post_origin(msg)
|
||||
|
||||
mimedecode_message_body(src, msg, filecb)
|
||||
end
|
||||
|
||||
return M
|
||||
|
Loading…
Reference in New Issue
Block a user