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:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Dependencies
|
- 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
|
- name: Install example site
|
||||||
run: ln -s ./docs/site-example ./site
|
run: ln -s ./docs/site-example ./site
|
||||||
- name: Lint Lua code
|
- name: Lint Lua code
|
||||||
@ -22,7 +22,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: sudo apt install shellcheck
|
run: sudo apt-get -y update && sudo apt-get -y install shellcheck
|
||||||
- name: Install example site
|
- name: Install example site
|
||||||
run: ln -s ./docs/site-example ./site
|
run: ln -s ./docs/site-example ./site
|
||||||
- name: Lint shell code
|
- name: Lint shell code
|
||||||
|
@ -44,7 +44,6 @@ $Id$
|
|||||||
|
|
||||||
<div class="gluon-page-actions">
|
<div class="gluon-page-actions">
|
||||||
<input type="hidden" name="step" value="2" />
|
<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%>" />
|
<input class="gluon-button gluon-button-submit" type="submit" value="<%:Upload image%>" />
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</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">
|
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" style="display:inline">
|
||||||
<input type="hidden" name="step" value="3" />
|
<input type="hidden" name="step" value="3" />
|
||||||
<input type="hidden" name="keepcfg" value="<%=keepconfig and "1" or "0"%>" />
|
<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%>" />
|
<input class="gluon-button gluon-button-submit" type="submit" value="<%:Continue%>" />
|
||||||
</form>
|
</form>
|
||||||
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" style="display:inline">
|
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" style="display:inline">
|
||||||
<input type="hidden" name="step" value="1" />
|
<input type="hidden" name="step" value="1" />
|
||||||
<input type="hidden" name="keepcfg" value="<%=keepconfig and "1" or "0"%>" />
|
<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%>" />
|
<input class="gluon-button gluon-button-reset" type="submit" value="<%:Cancel%>" />
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<form method="post" enctype="multipart/form-data" action="<%|url(request)%>" data-update="reset">
|
<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" />
|
<input type="hidden" name="<%=id%>" value="1" />
|
||||||
|
|
||||||
<div class="gluon-form" id="form-<%=id%>">
|
<div class="gluon-form" id="form-<%=id%>">
|
||||||
|
@ -184,9 +184,15 @@ local function dispatch(config, http, request)
|
|||||||
return
|
return
|
||||||
end
|
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
|
if not ok then
|
||||||
http:status(500, "Internal Server Error")
|
http:status(500, "Internal Server Error")
|
||||||
renderer.render_layout("error/500", {
|
renderer.render_layout("error/500", {
|
||||||
@ -208,6 +214,6 @@ return function(config, http)
|
|||||||
if not ok then
|
if not ok then
|
||||||
http:status(500, "Internal Server Error")
|
http:status(500, "Internal Server Error")
|
||||||
http:prepare_content("text/plain")
|
http:prepare_content("text/plain")
|
||||||
http:write(err)
|
http:write(err .. "\r\n")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -108,16 +108,11 @@ end
|
|||||||
-- o String value containing a chunk of the file data
|
-- o String value containing a chunk of the file data
|
||||||
-- o Boolean which indicates whether the current chunk is the last one (eof)
|
-- o Boolean which indicates whether the current chunk is the last one (eof)
|
||||||
local function mimedecode_message_body(src, msg, filecb)
|
local function mimedecode_message_body(src, msg, filecb)
|
||||||
|
local mime_boundary = (msg.env.CONTENT_TYPE or ''):match("^multipart/form%-data; boundary=(.+)$")
|
||||||
if msg and msg.env.CONTENT_TYPE then
|
if not mime_boundary then
|
||||||
msg.mime_boundary = msg.env.CONTENT_TYPE:match("^multipart/form%-data; boundary=(.+)$")
|
error("Invalid Content-Type found")
|
||||||
end
|
end
|
||||||
|
|
||||||
if not msg.mime_boundary then
|
|
||||||
return nil, "Invalid Content-Type found"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
local tlen = 0
|
local tlen = 0
|
||||||
local inhdr = false
|
local inhdr = false
|
||||||
local field = nil
|
local field = nil
|
||||||
@ -188,10 +183,10 @@ local function mimedecode_message_body(src, msg, filecb)
|
|||||||
local spos, epos, found
|
local spos, epos, found
|
||||||
|
|
||||||
repeat
|
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
|
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
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -250,20 +245,61 @@ local function mimedecode_message_body(src, msg, filecb)
|
|||||||
return true
|
return true
|
||||||
end
|
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
|
end
|
||||||
|
|
||||||
-- This function will examine the Content-Type within the given message object
|
-- This function will examine the Content-Type within the given message object
|
||||||
-- to select the appropriate content decoder.
|
-- to select the appropriate content decoder.
|
||||||
-- Currently only the multipart/form-data mime type is supported.
|
-- Currently only the multipart/form-data mime type is supported.
|
||||||
function M.parse_message_body(src, msg, filecb)
|
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
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
if msg.env.CONTENT_TYPE:match("^multipart/form%-data") then
|
check_post_origin(msg)
|
||||||
return mimedecode_message_body(src, msg, filecb)
|
|
||||||
end
|
mimedecode_message_body(src, msg, filecb)
|
||||||
end
|
end
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
Loading…
Reference in New Issue
Block a user