Skip to content

Commit

Permalink
feat(web): enforce same-origin for websocket and /vat post
Browse files Browse the repository at this point in the history
This prevents foreign web pages from attacking your ag-solo via
the browser.
  • Loading branch information
michaelfig committed Oct 15, 2019
1 parent 2101e20 commit a704120
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 6 deletions.
4 changes: 3 additions & 1 deletion lib/ag-solo/upload-contract.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ export default async function upload(basedir, args) {
}
}

const ws = new WebSocket(wsurl);
const originurl = new URL('/', wsurl);
originurl.protocol = 'https:';
const ws = new WebSocket(wsurl, { origin: originurl.href });
const exit = makePromise();
ws.on('open', async () => {
try {
Expand Down
59 changes: 55 additions & 4 deletions lib/ag-solo/web.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Start a network service
const path = require('path');
const http = require('http');
const express = require('express');
const WebSocket = require('ws');
const morgan = require('morgan');
Expand All @@ -22,17 +23,52 @@ export function makeHTTPListener(basedir, port, host, inboundCommand) {
),
);
app.use(express.json()); // parse application/json
const server = app.listen(port, host, () =>
console.log('Listening on', `${host}:${port}`),
);
const server = http.createServer(app);

// serve the static HTML for the UI
const htmldir = path.join(basedir, 'html');
console.log(`Serving static files from ${htmldir}`);
app.use(express.static(htmldir));

const validateOrigin = req => {
const { origin } = req.headers;
const id = `${req.socket.remoteAddress}:${req.socket.remotePort}:`;

if (!origin) {
console.log(id, `Missing origin header`);
return false;
}
const url = new URL(origin);
const isLocalhost = hostname =>
hostname.match(/^(localhost|127\.0\.0\.1)$/);
if (isLocalhost(host)) {
if (!isLocalhost(url.hostname)) {
console.log(id, `Invalid origin host ${origin} is not local`);
return false;
}
} else if (url.hostname !== host) {
console.log(id, `Invalid origin host ${origin}`);
}

if (url.protocol !== 'http:' && url.protocol !== 'https:') {
console.log(id, `Invalid origin protocol ${origin}`, url.protocol);
return false;
}
if (String(url.port) !== String(port)) {
console.log(id, `Invalid origin port ${origin}`, url.port);
return false;
}

return true;
};

// accept vat messages on /vat
app.post('/vat', (req, res) => {
if (!validateOrigin(req)) {
res.json({ ok: false, rej: 'Invalid Origin' });
return;
}

// console.log(`POST /vat got`, req.body); // should be jsonable
inboundCommand(req.body).then(
r => res.json({ ok: true, res: r }),
Expand All @@ -45,7 +81,22 @@ export function makeHTTPListener(basedir, port, host, inboundCommand) {
// GETs (which should return index.html) and WebSocket requests.. it might
// be better to move the WebSocket listener off to e.g. /ws with a 'path:
// "ws"' option.
const wss = new WebSocket.Server({ server });
const wss = new WebSocket.Server({ noServer: true });
server.on('upgrade', (req, socket, head) => {
if (!validateOrigin(req)) {
socket.destroy();
return;
}

wss.handleUpgrade(req, socket, head, ws => {
wss.emit('connection', ws, req);
});
});

server.listen(port, host, () =>
console.log('Listening on', `${host}:${port}`),
);

let lastConnectionID = 0;

function newConnection(ws, req) {
Expand Down
5 changes: 4 additions & 1 deletion provisioning-server/src/ag_pserver/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,10 @@ def ret(server_message):
# be routed to vat-provisioning.js and the pleaseProvision() method.
try:
resp = yield treq.post(controller_url, m.encode('utf-8'), reactor=reactor,
headers={b'Content-Type': [b'application/json']})
headers={
b'Content-Type': [b'application/json'],
b'Origin': [controller_url.encode('utf-8')],
})
if resp.code < 200 or resp.code >= 300:
raise Exception('invalid response code ' + str(resp.code))
rawResp = yield treq.json_content(resp)
Expand Down

0 comments on commit a704120

Please sign in to comment.