Skip to main content

Best Coding Practices While Working With Sensitive Client Data: Securing Your Application_third layer of security

When communicating with a database and in order for your data to remain safe, keep the following key points in mind:
To stop attackers from using SQL Injection, you have to escape all users' input so they can't inject SQL queries into your application(for example, during a login). In pretty much all of the database drivers, for all languages, there is an option to escape user input. For example in node-mysql for Node.js, instead of doing your queries like this:
1
connection.query('SELECT * FROM users WHERE name = \''+ username +'\' AND password = \''+ password '\';', function (err, rows, fields) {
You can automatically escape them using this syntax:
1
connection.query('SELECT * FROM users WHERE name = ? AND password = ?;', [ username, password ], function (err, rows, fields) {
Variables from the array provided as a second argument to the query method will be inserted in place of question marks in the query string and will be automatically escaped. The same can be done in PHP using PDO's prepared statements:
1
2
3
4
5
$stmt = $dbh->prepare("SELECT * FROM users WHERE name = ? AND password = ?;");
$stmt->bindParam(1, $username);
$stmt->bindParam(2, $password);
$stmt->execute();
Even if you escape your database queries, you never know if a bug in your application has let some malicious code through. If you have written your app in a way to where you always assume that the content in the database is safe, then your users are endangered. For example, this code:
1
2
3
4
5
6
7
if ($result = $mysqli->query('SELECT * FROM users WHERE name = "'. $mysqli->escape_string($username) .'";')) {
    if ($row = $result->fetch_assoc()) {
        if ($result = $mysqli->query('SELECT * FROM someData WHERE uid = '. $row['id'] .';')) {
            ...
        }
    }
}
Is unsafe! If something goes wrong and the id of the user contains some SQL injection code, then you are going to have problems. $row['id'] should also be escaped using the escape_string() method, like so:
1
2
3
4
5
6
7
if ($result = $mysqli->query('SELECT * FROM users WHERE name = "'. $mysqli->escape_string($username) .'";')) {
    if ($row = $result->fetch_assoc()) {
        if ($result = $mysqli->query('SELECT * FROM someData WHERE uid = '. $mysqli->escape_string($row['id']) .';')) {
            ...
        }
    }
}
Now imagine this situation: some genius hackers break through your carefully crafted security and get their hands on your database. They then begin to brute-force all of your users' passwords. If you used a salt when hashing them, you can sleep peacefully and assure your users that their data is safe. Because unless the attacker has a quantum computer in their basement, it will take them years to crack any of the passwords.
Using a salt means that you append a few random characters to the password before hashing (these characters are what is called the salt) and store them with the password. You may think that this is not very secure, since the attacker will know the salt if they gain access to the database. But since the salt is different for every user, even if two of them use the same password their hashes will be different. This forces the attacker to crack them one by one, which makes it very time consuming and usually not worth the time wasted. This also means that the attacker can't use rainbow tables or a dictionary to lookup the passwords.
First you should generate the salt. For example, you could usecrypto.randomBytes():
1
var crypto = require('crypto');
1
2
3
4
5
6
/*
    this should be done on request with the user's data for registration,
    instead of just adding it to the database
*/
crypto.randomBytes(16, function (e, salt) {
This function will throw an error if, according to the Node.js documentation, "there is not enough entropy to generate cryptographically strong data". In such a case, you should either try again or use crypto.pseudoRandomBytes(), which generates non-cryptographically strong random bytes, but since this will only happen occasionally, we can use it:
1
2
3
if (e) {
    salt = crypto.pseudoRandomBytes(16);
}
Now let's add the data. For simplicity, I'll only use a username, password and salt in this example:
1
2
3
4
5
6
    /* password and username should be extracted from request body */
    var hash = crypto.createHash('sha-256').update(password + salt.toString()).digest('hex');
    sql.query('INSERT INTO users VALUES(?, ?, ?);', [ username, password, salt ], function (err, rows, fields) {
        /* your other app logic here, what happes after the user data is into the database */
    });
});
When your user wants to login, you have to get the salt from the database and then check the password:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
sql.query('SELECT salt FROM users WHERE name = ?;', [ username ], function (err, rows, fields) {
    if (rows.length < 1) {
        /* the user does not exist */
    } else {
        var hash = crypto.createHash('sha-256').update(password + rows[0].salt).digest('hex');
        sql.query('SELECT 1 FROM users WHERE name = ? AND password = ?;', [ username, hash ], function (err, rows, fields) {
            if (rows.length < 1) {
                /* wrong password */
            } else {
                /* password ok */
            }
        });
    }
});
And that's pretty much it. Now let's see how we can do this using PHP.
In PHP this is much simpler, because the built in functions password_hash() andpassword_verify() automatically uses a salt and the hash returned contains it, so you don't even need another column in the database (or field in case of NoSQL). It would look something like this:
1
2
3
4
5
6
$stmt = $dbh->prepare("INSERT INTO users VALUES (?, ?);");
$stmt->bindParam(1, $username);
/* hash the password, the function adds random salt automatically */
$stmt->bindParam(2, password_hash($password));
$stmt->execute();
To check the login, you just have to call password_verify(). It automatically gets the salt from the hash so you only need to supply the hash from your database and the plaintext password to compare. The needed code, is as follows:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
$stmt = $dbh->prepare("SELECT password FROM users WHERE name = ?;");
$stmt->bindParam(1, $username);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) {
    /* check if the password matches */
    if (password_verify($password, $row['password'])) {
        /* password ok! */
    }
} else {
    /* the user does not exist */
}
This is one of the best defense mechanisms you can use to protect your machine and therefore your users. You need elevated privileges for a couple of things, mainly listening on port numbers lower than 1024 or messing with system files. But you should never run any app as root when you do not need to, just in case the attacker finds a bug in your code that allows them to execute commands on your server and if the code is running as a privileged user, it's game over. The attacker can do whatever they want and will probably be done before you even notice anything. This is why you should drop the privileges as fast as possible. In Node.js it would look like this:
1
2
3
4
5
6
var app = http.createServer(...);
...
app.listen(80); // listen on the 80 port, to do so you have to run your app as root user
process.setuid('app_user'); // drop privileges - switch to a non-root user - only POSIX platforms sadly
The process.setuid() function will change the user identity of the process to the one passed to it - it can be either a numerical ID or a username string (in the second case, this function will block while getting the ID of the user). The user should be unprivileged and only have access to the app-specific files, to limit the risk of giving the attacker access to anything else on your machine.
An Alternative Way: Using authbind
Some people say (for example in this comment on dropping root privileges), that the solution above is not perfect and they prefer to use authbind instead. It's your decision which one you choose to use and it pretty much depends on your setup. But anyways, authbind is a command that was designed for this purpose - it allows your app to bind to ports lower than 1024 without root privileges (so it's only covering that scenario). To use it, first create a file: /etc/authbind/byport/port, where port is the port number you want to bind to and make it executable by the user you will use, to execute your app. Then switch to your user and start the app like this:
1
authbind node yourapp.js
Or like this from root:
1
su -c 'authbind node yourapp.js' youruser
With this, you can accomplish the same end goal as with the POSIX solution, just now using authbind instead, if that's what you prefer.
So hopefully you've picked up some new techniques for working with sensitive data throughout these two articles. In the previous article we started off learning how to protect our data on the server by choosing the right server provider, updating our OS, securing ports and using an antivirus. In this article, we wrapped up our discussion by talking about securing our application itself through the use of proper database security, password salting, and user privileges.
Feel free to share your own security tips in the comments and thank you for reading.

Comments

Popular posts from this blog

World largest data sets open to the public | Business Intelligence | Data Warehouse | Data Mining

World largest data sets open to the public | Business Intelligence | Data Warehouse | Data Mining Data Sets available for different sectors as follows: Science & Technology    - World largest data sets open to the public | Business Intelligence | Data Warehouse | Data Mining Agricultural Experiments:  agridat {agridat}  (R) Climate data:  Temperature data (HadCRUT4)  and ftp://ftp.cmdl.noaa.go v/ Gene Expression Omnibus:  Home - GEO - NCBI Geo Spatial Data:  Data | GeoDa Center Human Microbiome Project:  Microbial Reference Genomes MIT Cancer Genomics Data:  Page on broadinstitute.org NASA:  Obtaining Data From the NSSDC NIH Microarray data:    ftp://ftp.ncbi.nih.gov/pu b/geo/D...  (R) Protein structure:  PSP benchmark Public Gene Data:  Browse literature or sequence neighbours Stanford Microarray Data:  Page on stanford.edu Social Sciences   - World largest data sets open to the public | Business Intelligence | Data Warehouse | Data Mining General S

Simple way 2 secure ur Privacy

Essential Checks Before Launching Your Website As ‘digital professionals’ –  Web Designers , Developers and Marketers – launching a new website is a daunting task, no matter how often you do it (like B.A.S.E. jumping). There’s lots that can go wrong, and the list of ‘ gotchas ‘ scales to the size and complexity of the project. This article is a checklist of common tasks that need to be completed before you hit the “GO” button.  A little preparation goes a long way  and could save you time and avoid unnecessary costs after you release your website. Upload a Favicon The ‘favicon’ appears to the left of the page title in the web browser, and your users will notice if your website doesn’t have one. They give your website credibility and help users navigate to your site when it’s open amongst their other tabs and bookmarks. Ensuring that your website has a favicon is probably the most basic of any task known to humanity, and yet it’s so frequently overlooked. STEP ONE: CRE

AWS Cloud Architecture for Web Hosting | Key Components of an AWS Web Hosting Architecture

Security Architecture of AWS | Amazon Web Server Working of AWS Architecture. Content Delivery Edge caching is still relevant in the Amazon Web Service cloud computing infrastructure. Any existing solutions in your web application infrastructure should work just fine in the AWS cloud. One additional option, however, is made available when using AWS, which is to utilize the Amazon CloudFront service1 for edge caching your website Like other Amazon Web Services, there are no contracts or monthly commitments for using Amazon CloudFront – you pay only for as much or as little content as you actually deliver through the service. Managing Public DNS  Moving a web application to the AWS cloud requires some DNS changes to take advantage of the multiple availability zones that AWS provides. To help you manage DNS routing, AWS provides Amazon Route 534 , a highly available and scalable DNS web service. Queries for your domain are automatically routed to the nearest DNS server and th