I'm writing a socket.io based webcam streaming application that should emit a 'streamComplete' event when the feed times out. The problem is, image resources requests don't seem to send the same headers/cookies as a normal AJAX request. This seems to make it impossible for socket.io to identify the request with a socket so that it can later tell the client that the stream is complete.
On the server side, I have a node.js proxy that serves up mjpeg (moving jpeg) images. When a client selects a feed in the app, the 'src' attribute of an img element in the DOM changes to point to the route of the camera feed.
I've looked into the HTML5 CORS enabled image specification which appears to try to address this issue, but as far as I can tell the feature isn't fully implemented in most browsers. If it is, I can't figure out how to allow those requests on the node.js side -- whenever I add crossorigin='use-credentials' to the image, an error saying
"Cross-origin image load denied by Cross-Origin Resource Sharing
policy."
Obviously I should be able to get around that since I control the server side, but the following node header rules doesn't seem to be working even though they seem extremely relaxed:
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With, XMLHttpRequest, X-HTTP-Method-Override, Content-Type");
res.header('Access-Control-Allow-Methods', 'POST, GET, PUT, OPTIONS');
One option that I have considered is tacking on the socketID to the image request and registering the listener on req.io.sockets.sockets[req.body.socketID]. That seems a little hacky though and I'm sure could bring up other concerns like race conditions. Here's the portion of my code where I am trying to accomplish this.
app.use(express.bodyParser());
app.use(express.cookieParser());
app.use(express.session({secret: 'blahblahblah'}));
// Allow all cross origin requests.
app.use(allowCrossOrigin);
app.get('/streams/:cam_id/:framerate', function(req, res,next) {
var camId = req.params.cam_id
, framerate = req.params.framerate
, type = (framerate === 0) ? 'jpeg' : 'mjpeg'
, reqString = 'http://blahblah.com/feeds/'
, proxy;
// Query database for stream info.
var query = new drupalInterface.Query(camId);
// When query comes back build the URL request string and create proxy.
query.on('results:available', function(results) {
var reqArr = [
results.site_name
, results.camera_name
, type
, framerate
];
reqString = reqString + reqArr.join('/') + '?status_frame=true&random=' + Math.random();
// Create new proxy for current client.
req.proxy = new MjpegProxy(reqString);
// Make request to telepresence server.
req.proxy.proxyRequest(req, res, next);
// Forward to express.io route for emitting stream event info to clients.
req.io.route('initializeProxyStream');
});
});
app.io.route('initializeProxyStream', function(req) {
var socket = app.io.sockets.sockets[req.query.socketID];
// If connecting directly to /stream don't try to listen for socket connection.
if(req.io) {
req.proxy.once('streamComplete', function() {
socket.emit('streamComplete');
});
}
});
I'm not sure express.io is even needed for what I am doing, but it seems like it makes sense in this context.
Was I on the right track with the crossorigin attribute? Is there some middleware that I'm missing?
Related
I log all 404s on my website. I keep getting them for pages I haven't linked to, and it's clearly someone (a bot) trying to find admin pages / secure files on my site such as /wp-admin.php;
router.get('/wp-admin.php', function(req, res, next) {});
I tried this and it doesn't seem to hold up the server, it just outputs something like this a minute later:
GET /wp-admin.php - - ms - -
Is there any detriment to adding routes such as that, where no response is sent, possibly wasting their time?
router.get('/wp-admin.php', function(req, res, next) {});
This will cause express to time out and close the connection. This will make Denial of Service attack easier for hackers and jam up your node server.
You can always use some kind of rate limiters to prevent continuous request from a certain IP.
express-rate-limit
is a can be used for this. It is simple express middleware
As noted in the already accepted answer, an Express route like that will leave you vulnerable.
I recommend going one step further and tearing down those requests using req.destroy.
I'm not sure of the implications of Express being included, here, though. For example, is the request body being read automatically by a middleware upstream of this request handler you've shown? If so, that would be an attack vector that makes the mitigation I'm suggesting useless.
Regardless, to demonstrate what I am suggesting with a vanilla HTTP server:
var h = require('http')
h.createServer(function(req, res) {
// tear down the socket as soon as the request event is emitted
req.destroy()
}).listen(8888, function() {
// send a request to the server we just created
var r = h.request({port: 8888})
r.on('response', console.log.bind(console, 'on_response'))
r.on('error', console.log.bind(console, 'on_error'))
r.on('timeout', console.log.bind(console, 'on_timeout'))
// abort will be emitted to the caller, but nothing else
r.on('abort', console.log.bind(console, 'on_abort'))
r.end()
})
You could also call socket.destroy in the connection event of the HTTP server if you're able to identify the calling agent as a bot (or whatever) somehow.
var h = require('http')
h.createServer(function(req, res) {
res.send('foo')
}).on('connection', function(socket) {
// pretend this ip address is the remote address of an attacker, for example
if (socket.remoteAddress === '10.0.0.0') {
socket.destroy()
}
}).listen(8888, function() {
// send a request to the server we just created
var r = h.request({port: 8888})
r.on('response', console.log.bind(console, 'on_response'))
r.on('error', console.log.bind(console, 'on_error'))
r.on('timeout', console.log.bind(console, 'on_timeout'))
// abort will be emitted to the caller, but nothing else
r.on('abort', console.log.bind(console, 'on_abort'))
r.end()
})
I would like to require pages in my Node.js server based on the requested URI.
However I concern that this could be a severe security issue since user can inject some malicous chars into the url, something like ../../ and reach to my root server point and reveal all of the code.
So just like throwing a bottle of water to a big fire, I have eliminated the option to send . to the request.
This is not a silverbullet, probably :)
Maybe is there some standard/best practice/guide or keypoints about URI sanitizing in REST API based on Node.js?
Edit - here the code uses the require
// app.js
app.use(require('./services/router')(app));
// router.js middleware
function router(app) {
return function(req, res, next) {
try {
// checking for . in the url
if (req.url.indexOf(".")!=-1) cast.badRequest();
// req.url.split('/')[2] should be customers, users or anything else
require('../../resources/' + req.url.split('/')[2] + '/' + req.url.split('/')[2] + '-router')(app);
next();
} catch(err) { cast.notFound(); }
}
}
module.exports = router;
// rides-router.js (this could be users-router.js or customers-router.js)
module.exports = function(app) {
// GET ride - select a ride
app.get("/v1/rides/:id", dep.verifyToken(), require('./api/v1-get-ride'));
// POST ride - insert a new ride
app.post("/v1/rides", dep.verifyToken(), require('./api/v1-set-ride'));
app.use((req, res, next) => {
cast.notFound();
});
}
You asked how to do it safer. My recommendation is that you put all the resources in an array and run all the app.use() statements with one loop that pulls the resource names from the array at server startup.
I don't like running synchronous require() during a request and I don't like loading code based on user specified characters. Both are avoided with my recommendation.
// add routes for all resources
const resourceList = ['rides', 'products', ...];
for (let r of resourceList) {
app.use(`/${r}`, require(`./resources/${r}/${r}-router`));
}
This seems like less code and 100% safe and no running of synchronous require() during a request.
Advantages:
Fully whitelisted.
No user input involved in selecting code to run.
No synchronous require() during request processing.
All routes installed at server initialization time.
Any errors in route loading (like a missing route file) occur at server startup, not during a user request.
I am using node.js and express to make a CRUD web app, and I am trying to work out how to get a flash message to appear on a data entry form when server side validation fails. I am using express-flash for the flash messages.
The validation code works, returning errors, and I create the flash message:
var errors = validateForm(req);
if(errors){
req.flash('info',errors);
res.render('edit', {messages: req.flash('info')});
}
And display the message in the edit.jade file:
if messages.info
p #{messages.info}
The problem is that I am editing a specific object, and the url is not /edit, but /edit/objectID. The get for the page is like this:
router.get('/edit/:id', function(req, res) {
var db = req.db;
var collection = db.get('mydb');
collection.find({ID: req.params.id},{},function(e,docs){
res.render('edit', {"object" : docs});
});
});
Is it possible to add a flash message for server side validation after a POST has been sent without losing the page ID? I don't need all the data from the edit, there will be client side validation to hopefully pick up any errors, but I would like server side validation errors to cause the user to land on the same object page as they left.
You can declare your post route as:
router.post('/edit/:id', function(req, res) {
// logic to check for errors
// if errors, set flash message and
// redirect to /edit/:id
});
Then on, you can use the ID in the post route to redirect to the edit page of the same resource that the user posted the form for. You will also be able to access the flash error messages in the template.
* Edit *
In order to use flash on redirect you would need to add it the middleware.
app.configure(function() {
app.use(express.cookieParser('keyboard cat'));
app.use(express.session({ cookie: { maxAge: 60000 }}));
app.use(flash());
});
From a tutorial on Sails, this is a Sails policy - essentially Express middleware:
module.exports = function(req, res, next) {
res.locals.flash = {};
if (!req.session.flash) return next();
res.locals.flash = _.clone(req.session.flash)
// clear flash
req.session.flash = {};
next();
};
I'm totally lost:
What's the difference between req.locals.flash and req.session.flash?
Why do the response locals.flash have to be cleared right off the bat?
Why are response locals filled with cloned request session flash?
What's the difference between req.locals.flash and req.session.flash?
req.locals.flash will available in its view, but the req,session
relates with the session of browser.
More info locals vs session
Why do the response locals.flash have to be cleared right off the bat?
Make sure that the error message just display one time only.
Why are response locals filled with cloned request session flash?
Cause if not the locals will keep the pointer of the session. So if
the "req.session.flash = {};", the locals will also be empty
This is a policy, it will be invoked each time you visit a url(depends on your config/policies.js), what it does is to consume the message.
Clearing req.session.flash is to make sure the message is only displayed once, clearly you don't want to be haunted by the message again and again. :D
Hope that helps.
Now, after some hours of playing around with nodejs and socket.io, I'm getting a couple more problems - one being, that I need to get the sessionID inside node.js, whitout using app.get('/' ... - since that event doesnt seem to fire when socket.io connects, it only fires .on('connection', function( ...
var express = require('express')()
express.set('port', process.env.PORT || 8080)
var server = require('http').createServer(express)
var socket = require('socket.io').listen(server)
server.listen(express.get('port'))
// this event is fired, get('/', ... isnt!
server.on('connection', function(stream) {
// ??
} )
The Session is initially created by the PHP application, as the user logs in. Session data is stored in the database, and the key I need to access that data is the SESSION ID. What's the easiest way to get to it? Like mentioned, I found a couple examples that used app.get('/' ... but I couldnt get that event to fire at all.
Thanks.
If the session data is being stored as a cookie (most likely), then you should be able to re-parse that data during the socket handshake. I posted code for that on this answer, but copied the code here as well:
io.configure(function () {
io.set('authorization', function (handshakeData, callback) {
var cookie = handshakeData.headers.cookie;
// parse the cookie to get user data...
// second argument to the callback decides whether to authorize the client
callback(null, true);
});
});
If the session data is being propagated in the URL, then you may be able to gather this information from handshakeData.url or handshakeData.query. You'll probably have to do your own experimentation.