Skip to content
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

Merged
merged 3 commits into from
Jan 19, 2023

Conversation

mnutt
Copy link
Contributor

@mnutt mnutt commented Apr 8, 2021

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.

@mnutt
Copy link
Contributor Author

mnutt commented Apr 13, 2022

@petrsloup is there any interest in this PR?

@acalcutt
Copy link
Collaborator

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?

@mnutt
Copy link
Contributor Author

mnutt commented Sep 23, 2022

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.

@mnutt
Copy link
Contributor Author

mnutt commented Sep 24, 2022

@acalcutt it's rebased and the merge conflicts are resolved.

@mancontr
Copy link

mancontr commented Jan 17, 2023

I think that the PR does not cover all the places where the problem happens. The same changes are needed on data.tmpl too. Also, lines 10 and 11 on both template files also use key_query unsafely, and would need to be fixed.

Vulnerability scanners are picking this issue already, so it would be awesome if the PR could be updated and merged...

@acalcutt
Copy link
Collaborator

acalcutt commented Jan 17, 2023

@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?

@mnutt
Copy link
Contributor Author

mnutt commented Jan 18, 2023

What do you think of using URL() here instead of a regex? Released on all modern browsers as of 2014 so it seems fairly safe.

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.

@acalcutt
Copy link
Collaborator

acalcutt commented Jan 18, 2023

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.

@mnutt
Copy link
Contributor Author

mnutt commented Jan 18, 2023

Makes sense, I'll switch back to regex for the time being.

@acalcutt
Copy link
Collaborator

That {&key_query} is used all through the head of the viewer template too... hmm

@mnutt
Copy link
Contributor Author

mnutt commented Jan 18, 2023

It looks like the templating engine knows a little bit about XSS and is smart enough to escape {{key_query}} when it's actually inside an HTML attribute. The only really dangerous one is where we use a script tag to choose which script to inject, because to the templating engine just sees it as a javascript string and leaves it alone. I've removed {{key_query}} from all of the static assets and it looks like the XSS hole is closed.

@mnutt mnutt requested a review from acalcutt January 18, 2023 15:49
@acalcutt
Copy link
Collaborator

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.

@acalcutt
Copy link
Collaborator

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?

@acalcutt
Copy link
Collaborator

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.

@vinayakkulkarni
Copy link
Contributor

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, key key in the key=value key-pair should be dynamic ?

@vinayakkulkarni
Copy link
Contributor

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.

Maybe this is the remanent code which maptiler/server uses key for?

@acalcutt
Copy link
Collaborator

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, key key in the key=value key-pair should be dynamic ?

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
Nginx can be used to add protection via https, password, referrer, IP address restriction, access keys, etc.

@mnutt
Copy link
Contributor Author

mnutt commented Jan 18, 2023

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 key query param is to receive it as a query param and reflect it into the template?

@acalcutt
Copy link
Collaborator

acalcutt commented Jan 18, 2023

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.

@mancontr
Copy link

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.

@acalcutt acalcutt merged commit 78c1777 into maptiler:master Jan 19, 2023
if (preference == 'vector') {
maplibregl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js{{&key_query}}');
maplibregl.setRTLTextPlugin('{{public_url}}mapbox-gl-rtl-text.js' + keyParam);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
var url = '{{public_url}}data/{{id}}.json' + keyParam;
var url = `{{public_url}}data/{{id}}.json${keyParam}`;

@@ -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}` : '';
Copy link
Contributor

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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
url: '{{public_url}}data/{{id}}.json' + keyParam
url: `{{public_url}}data/{{id}}.json${keyParam}`

Comment on lines +8 to +11
<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>
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

XSS vulnerability
4 participants