bornytm
bornytm

Reputation: 813

Database Exposure: Best Practices

I'm a relatively new web programmer and I'm currently working on my first major project. I'm using angular, express (on top of node), and the graph database neo4j. Right now I'm trying to determine the best (in terms of security and speed optimization) way to set up how the web app interacts with the database.

Right now I feel like I'm going into this somewhat blindly- What I'm looking for is a guide of best practices, security issues to take into account, and any other relevant advice or pitfalls to be aware of in setting up a web app backend.

To put this into a bit more concrete terms I'll give you an idea of how I'm setting up routes right now. The following are the routes setup in the app.js file.

    //match database query functions
function dataQuery(req, res) {
    var func = database[req.param('query')];
    func(req, res);
}
    //match database create functions
function dataCreate(req, res) {
    var func = database[req.param('create')];
    func(req, res);
}
//handle data queries
app.get('/query/:query', dataQuery);

//handle adding new content
app.post('/create/:create', dataCreate)

Essentially I have it set up so that I POST or GET to a url that just goes and executes a function. I'm essentially naming the function I want to run in the url: /query/theNameOfTheFunction. These functions then go and either build a cypher query (neo4j's query language) utilizing information in the request to interact with the database or handles things like adding user uploaded images.

Example: Creating Content (URL: /query/createContent)

exports.createContent = function (req, res) {

    var content = JSON.parse(req.query.content);
    var query = ("CREATE (n:Content {Title: {title}, URL: {url}, Description: {description}, Source: {source}, Links: {links}, Value: {valueStatement} })");

    query = query.replace("{title}", "\"" + content.title + "\"");
    query = query.replace("{url}", "\"" + content.url + "\"");
    query = query.replace("{description}", "\"" + content.description + "\"");
    query = query.replace("{source}", "\"" + content.source + "\"");
    query = query.replace("{links}", "\"" + content.links + "\"");
    query = query.replace("{valueStatement}", "\"" + content.valueStatement + "\"");

   db.query(query, function (err, results) {
       if (err) {res.send()};
       res.send();
   });
}

Here I've got a template for the query and just drop in user generated information using replace.

Example: Adding images to server (URL: /create/addImage)

exports.addImage = function (req,res) {
    var url = req.query.url;
    var fileName = req.query.fileName;

    console.log(req.query);
    request(url).pipe(fs.createWriteStream("./img/submittedContent/" + fileName));
    res.send();
}

It seems that this approach is probably not very scalable but I'm not sure how to best organize the code on the server side.

One other specific example I would like to mention is the following case. The query itself is complicated and I've pushed creating it to the client side for now (the query looks for content related the terms that the user has selected and varies in length accordingly). The client sends the query that is created it is passed into the neo4j api. Obviously there are concerns here- if the user is able to define the query they could perform any action on the database (deleting everything or whatever). I'm not clear on how someone could go about doing this exactly, but it certainly seems feasible.

exports.getContent = function (req, res) {
    var query = req.query.query;

        //would checking for black/white list key terms be enough security? (remove, create, set, etc)

    db.query(query, function (err, results) {
        if (err) {throw err};
        res.send(results);
    });
}

Am I going about this stuff completely wrong headed? I've never gotten a formal introduction to server side scripting and am only going off of things I've read. I would like to do it the 'right way' but I need to know what that way is first...

Upvotes: 1

Views: 538

Answers (1)

robertklep
robertklep

Reputation: 203409

Just some random pointers:

  • I would suggest setting up a RESTful web API to handle the communication between Angular and your database; it takes the hassle out of having to invent all the routes yourself and it also means you can use great libraries like Restangular (for the client) and Restify (for the server) to handle the communications;
  • not sure which Neo4j driver you're using, but I'm pretty sure they all support parameterized queries, meaning that you don't need to do all those query.replace() calls (see);
  • depending on the number of images that might get uploaded, storing them in the filesystem might be okay, although you should never trust the passed filename; if you want a bit more scalability, you could consider using MongoDB's GridFS;
  • never trust queries being passed from the client to be performed on the server; if you can build the query on the client side, you can also build it on the server side with information passed from the client to the server (again, use parameterized queries);

Upvotes: 3

Related Questions