3
\$\begingroup\$

I'm currently learning Node.js. To practice, I've decided to create a login server. The program goes as follows:

  1. User connects to local website (10.0.0.79, props to this users answer for helping me figure out how to set it up)
  2. User is presented with a login screen, with fields username and password.
  3. User enters the username and password, and the server checks if there are any matches, reading from a JSON file, creds.json.

This is the file layout:

NodeServer
    |-node_modules
        |-(packages installed with node)
    |-creds.json
    |-index.html
    |-index.js
    |-log.txt
    |-package-lock.json
    |-package.json

There are a few areas I would like feedback on:

  • SECURITY: Is my website secure in any way, shape, or form? I'm wondering if I could implement any security measures other than the ones that are built in to the methods provided by Node.js. Also, I know the passwords are plainly obvious to guess, but they are like that to ensure logging in as different users works.
  • EFFICIENCY: Is how I'm checking usernames and password efficient? Is there any better way to do this?
  • BUILDING: Is how I loaded my website acceptable? Reading from a file and then ending the response?
  • ASYNC/SYNC: I know I preform async and sync calls at the same time. Is there any problem to this?
  • LOGGING: I log all connections to the server, and all login attempts. Is this a good practice, or am I overdoing what logging is supposed to accomplish?

Any and all feedback is appreciated and considered!

index.js

/* NODE START */
const http = require('http');
const fs = require('fs');
const url = require('url');
const port = 80;

var logFile = "log.txt";

const server = http.createServer(function(request, response) {

    if(request.method = 'POST') {

        let body = '';
        request.on('data', chunk => {
            body += chunk.toString();
        });
        request.on('end', () => {
            //parse data
            var credentials = body.split('&');
            //Use below conditional in case there is an empty login attempt, or
            //there is a page reload that contains no data
            if(!(credentials[0] === '')) {
                var username = credentials[0].substring(9);
                var password = credentials[1].substring(9);
                console.log(username + " " + password);
                login(username, password, request);
            }
        });

    }

    //LOADING WEBPAGE
    response.writeHead(200, { 'Content-Type': 'text/html' });
    fs.readFile('index.html', function(error, data) {
        if(error) {
            response.writeHead(404);
            response.write("Error: File Not Found");
        } else {
            response.write(data);
        }
        response.end();
    });

    //LOGGING
    var address = request.socket.remoteAddress;
    var logData = "[*] CONNECTION \n";
    logData += "\t[+] FROM - " + address + "\n";
    logData += "\t[+] DATE - " + getDate() + "\n";
    logData += "\t[+] TIME - " + getTime() + "\n";
    if(!(request.url === '/favicon.ico')) {
        fs.appendFileSync(logFile, logData); 
    }
});

server.listen(port, '0.0.0.0');
console.log("Server is listening on port " + port);

function getDate() {

    var date = new Date();

    var year = date.getFullYear();

    var month = date.getMonth() + 1;
    month = (month < 10 ? "0" : "") + month;

    var day  = date.getDate();
    day = (day < 10 ? "0" : "") + day;

    return month + "." + day + "." + year;
}

function getTime() {

    var date = new Date();

    var hour = date.getHours();
    hour = (hour < 10 ? "0" : "") + hour;

    var min  = date.getMinutes();
    min = (min < 10 ? "0" : "") + min;

    var sec  = date.getSeconds();
    sec = (sec < 10 ? "0" : "") + sec;

    return hour + ":" + min + "." + sec;
}

function login(username, password, request) {
    let rawdata = fs.readFileSync('creds.json');
    let creds = JSON.parse(rawdata);
    let users = ['ben', 'hannah', 'kristen', 'tony', 'katherine'];
    for(var i = 0; i < users.length; i++) {
        var person = creds[users[i]];
        if(person['username'] === username && person['password'] === password) {
            console.log("User [" + username + "] logged in!");

            //LOG USER LOGIN
            var userLoginData = "[*] LOGIN \n";
            var loginAddress = request.socket.remoteAddress;
            userLoginData += "\t[+] USER - " + username + "\n";
            userLoginData += "\t[+] FROM - " + loginAddress + "\n";
            userLoginData += "\t[+] DATE - " + getDate() + "\n";
            userLoginData += "\t[+] TIME - " + getTime() + "\n";
            fs.appendFileSync(logFile, userLoginData); 

            break;
        }
    }

}

/* NODE END */

/* 
STATUS CODES
2xx: Success
 - 200: OK
 - 201: Created
 - 204: No Content
 - 202: Accepted
3xx: Redirection
 - 304: Not Modified
 - 301: Moved Permanently
4xx: Client Error
 - 404: Not Found
 - 401: Unauthorized
 - 400: Bad Request
 - 403: Forbidden
 - 409: Conflict
*/

index.html

<!DOCTYPE html>
<html lang="en-US">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Login Portal</title>
        <style type="text/css">
            input[type="text"], input[type="password"] { 
                border-radius: 10px;
                width: 230px;
                height: 35px;
                font-size: 30px;
                margin: 5px;
            }
            input[type="submit"] {
                border-radius: 10px;
                width: 100px;
                height: 50px;
                font-size: 20px;
            }
            label { font-size: 30px; }
            #centered { text-align: center; margin-top: 200px; }
        </style>
    </head>
    <body bgcolor="pink">
        <div id="centered">
            <h1>Login Portal</h1>
            <form method="post" action="/">
                <label>Username:</label>
                <input type="text" name="username"><br>
                <label>Password:</label>
                <input type="password" name="password"><br>
                <input type="submit">
            </form>
        </div>
    </body>
</html>

creds.json

{
    "ben": {
        "username": "benus123",
        "password": "benpw123"
    },
    "hannah": {
        "username": "hannahus123",
        "password": "hannahpw123"
    },
    "kristen": {
        "username": "kristenus123",
        "password": "kristenpw123"
    },
    "tony": {
        "username": "tonyus123",
        "password": "tonypw123"
    },
    "katherine": {
        "username": "katherineus123",
        "password": "katherinepw123"
    }
}

Excerpt from the log file

[*] CONNECTION 
    [+] FROM - 10.0.0.79
    [+] DATE - 06.29.2019
    [+] TIME - 22:54.38
[*] LOGIN 
    [+] USER - benus123
    [+] FROM - 10.0.0.79
    [+] DATE - 06.29.2019
    [+] TIME - 22:54.38
\$\endgroup\$
5
+50
\$\begingroup\$

Just addressing one of your questions.

"SECURITY: Is my website secure in any way, shape, or form?"

No it is not!!!

Reasons

  1. Insecure transport. You should never send private data via an unsecured protocol. HTTP will let anyone see all the data communicated between you (server) and the client. YOU MUST USE HTTPS or an alternative high level encryption when communicating any form of private data.

    Node.js supports HTTPS

  2. Insecure Data Store. The file creds.json is open for anyone that can gain access to it. You should NEVER store private data unencrypted no mater how secure you think your server may be.

    Node.js provides an encryption module you can use to secure server side data.

  3. Insecure source code. You have user names in the source code, this should never be done. There should be only one source of private data (see point 2 above)

  4. Insecure logging. You should never log data that contains private client data, or log information in such a way such that a reference/association can be made between a client and logged data without access to encryption keys.

Private data

You must consider all data related to a client as extremely sensitive. Its not only the password but handles (usernames), IP addresses, log on/off time/dates and more.

My advice is DON'T attempt to create your own authentication and log in system. Use existing services and/or systems.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.