-
Notifications
You must be signed in to change notification settings - Fork 614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix xss due to handlebars variables in javascript #535
Conversation
@petrsloup is there any interest in this PR? |
I'm guessing this is still an issue in my template, since I didn't protect key. Do you think this fix still should be fixed this way? Is this one of those things you were asking about doing on the frontend? |
Yes, a frontend framework would resolve this but I think the most straightforward fix is probably just applying the above. I can rebase it off of latest upstream. |
@acalcutt it's rebased and the merge conflicts are resolved. |
I think that the PR does not cover all the places where the problem happens. The same changes are needed on Vulnerability scanners are picking this issue already, so it would be awesome if the PR could be updated and merged... |
@mnutt , do you want to take a look at the other template file mentioned? Do you still think regex is the way to go with this if it is needed in two places? Didn't we use a sanitize plugin somewhere else? |
What do you think of using My hope is that we can actually just get rid of all of the instances where we merge the key into the static css/js URLs, although there may be some reason for it I don't yet understand. |
I'd say in the current release, one of the aims was to maintain internet explorer compatibility... so I'm not sure URL() would work there would it? On the topic of IE support though, maybe in a next PR we can start getting rid of IE support in these templates and simplify them a bit. Maplibre-gl currently works in IE using my combability library which would be nice to get rid of and leaflet is on their last version with IE support (next release drops IE). Maybe this would be a 5.x release since it dropping IE support is a major change I think. I do think for now, for this PR though, maybe we should wait on URL() and put it in the dropping IE PR. |
Makes sense, I'll switch back to regex for the time being. |
That {&key_query} is used all through the head of the viewer template too... hmm |
It looks like the templating engine knows a little bit about XSS and is smart enough to escape |
Thanks, I think it makes sense to remove it like you did. As for the injecting script, that is part of the IE support that I would like to remove in a future PR. |
I wish I knew more about how key is used in real life in relation to tileserver-gl... can tileserver-gl require a key right now? |
The only scenario I can think of where not having the key could fail is if you are behind some type of proxy server that requires the key to access the assets... (like haproxy/nginx level blocking). Like if you were blocking access to your own server by key. |
but even then, |
Maybe this is the remanent code which maptiler/server uses key for? |
I was more talking about the urls in head we are removing the key from. If the key was required to access those files, then not having it would break the view pages. For example, the documentation at https://maptiler-tileserver.readthedocs.io/en/latest/deployment.html#securing suggests |
Hmm, I had assumed that meant nginx could do that stuff as its own thing rather than anything relating to the query param. Currently it looks like the only thing the app does with |
I'm honestly not sure if that was the intent. I have seen that before where you need the key to access any resource. I'm just thinking, if tileserver was behind a proxy like that had required "access keys", you may need the keys to access any file hosted in that server, like those files in head. So if your were accessing one of these pages and specified an access key, it would need to pass it to the other resources on the page so it could access those files. That could be why this was added here, since that is similar to what their pay service does. |
It's likely it was used as a key for cache busting purposes, so it can be changed whenever it was needed to have browsers reload all assets. This is a very common usage for such a parameter. |
Signed-off-by: Michael Nutt <michael@nuttnet.net>
Signed-off-by: Michael Nutt <michael@nuttnet.net>
if (preference == 'vector') { | ||
maplibregl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js{{&key_query}}'); | ||
maplibregl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js' + keyParam); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maplibregl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js' + keyParam); | |
maplibregl.setRTLTextPlugin(`{{public_url}}mapbox-gl-rtl-text.js${keyParam}`); |
var map = new maplibregl.Map({ | ||
container: 'map', | ||
style: '{{public_url}}styles/{{id}}/style.json{{&key_query}}', | ||
style: '{{public_url}}styles/{{id}}/style.json' + keyParam, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style: '{{public_url}}styles/{{id}}/style.json' + keyParam, | |
style: `{{public_url}}styles/{{id}}/style.json${keyParam}`, |
@@ -49,7 +54,7 @@ | |||
new L.Control.Zoom({ position: 'topright' }).addTo(map); | |||
|
|||
var tile_urls = [], tile_attribution, tile_minzoom, tile_maxzoom; | |||
var url = '{{public_url}}styles/{{id}}.json{{&key_query}}'; | |||
var url = '{{public_url}}styles/{{id}}.json' + keyParam; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var url = '{{public_url}}styles/{{id}}.json' + keyParam; | |
var url = `{{public_url}}styles/{{id}}.json${keyParam}`; |
var map = L.map('map', { zoomControl: false }); | ||
new L.Control.Zoom({ position: 'topright' }).addTo(map); | ||
|
||
var tile_urls = [], tile_attribution, tile_minzoom, tile_maxzoom; | ||
var url = '{{public_url}}data/{{id}}.json{{&key_query}}'; | ||
var url = '{{public_url}}data/{{id}}.json' + keyParam; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
var url = '{{public_url}}data/{{id}}.json' + keyParam; | |
var url = `{{public_url}}data/{{id}}.json${keyParam}`; |
public/templates/data.tmpl
Outdated
@@ -76,11 +80,15 @@ | |||
<h1 style="display:none;">{{name}}</h1> | |||
<div id='map'></div> | |||
<script> | |||
const { searchParams } = new URL(document.location); | |||
const accessKey = searchParams.get('key'); | |||
const keyParam = accessKey ? `?key=${accessKey}` : ''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the ?
would cause issue cause it could be another search param already present, then that would be:
base_url/index.json?path=...&?key=accessKey
, instead we should check if there are any searchParams and then add ?key=${accessKey}
if none else &key=${accessKey}
@@ -44,7 +48,7 @@ | |||
sources: { | |||
'vector_layer_': { | |||
type: 'vector', | |||
url: '{{public_url}}data/{{id}}.json{{&key_query}}' | |||
url: '{{public_url}}data/{{id}}.json' + keyParam |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
url: '{{public_url}}data/{{id}}.json' + keyParam | |
url: `{{public_url}}data/{{id}}.json${keyParam}` |
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl.css" /> | ||
<link rel="stylesheet" type="text/css" href="{{public_url}}maplibre-gl-inspect.css" /> | ||
<script>if (typeof Symbol !== 'undefined') { document.write('<script src="{{public_url}}maplibre-gl.js"><\/script>'); } else { document.write('<script src="{{public_url}}maplibre-gl-compat.js"><\/script>'); }</script> | ||
<script>if (typeof Symbol !== 'undefined') { document.write('<script src="{{public_url}}maplibre-gl-inspect.min.js"><\/script>'); } else { document.write('<script src="{{public_url}}maplibre-gl-inspect-compat.min.js"><\/script>'); }</script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhh, this is causing issue with serving the static assets via a proxy (Kong) 😭 as query
has a custom access_key
which is required in each request served.
Fixes #532.
Using handlebars within javascript is pretty difficult to protect against, so this sidesteps it by using javascript to pull the query param off the page location and append to the RTL URL.
I used a regex because I thought perhaps this page was aiming for compatibility with very old browsers. If that's not the case, we could parse using
URLSearchParams
or something.