Logging

Logging should always be handled by the application and should not rely on server configuration.

All logging should be implemented by a master routine on a trusted system and developers should ensure that no sensitive data is included in the logs (e.g. passwords, session information, system details, etc.) nor is there any debugging or stack trace information.

Additionally, logging should cover both successful and unsuccessful security events with an emphasis on important log event data.

Important event data most commonly refers to:

  • All input validation failures
  • All authentication attempts, especially failures
  • All access control failures
  • All apparent tampering events, including unexpected changes to state data
  • All attempts to connect with invalid or expired session tokens
  • All system exceptions
  • All administrative functions, including changes to security configuration settings
  • All backend TLS connection failures and cryptographic module failures

Below is a brief source code sample using the popular winston package.

const winston = require('winston');
winston.add(winston.transports.File, {filename: 'my-log.log'});

// check login attempt status
switch (loginAttemptStatus) {
  case LOGIN_SUCCESS:
    // log successful login
    winston.log('info', 'User logged in!');

    // using winston.info()
    winston.info('User logged in!');
    break;
  case LOGIN_FAILURE:
    // log failed login attempt
    winston.warn("Unsuccessful login.");
    break;
  default:
    winston.error("Unknown login status");
  }

Another important aspect of logging is log rotation. This can be added to the above sample, using the winston-daily-rotate-file package.

As alternative packages for logging you may want to have a look at

In case of HTTP request logging, morgan is the most popular package. Its usage is very simple, as demonstrated below using Express Node.js framework

const express = require('express');
const morgan = require('morgan');

const app = express();

app.use(morgan('combined'));

app.get('/', (req, res) => {
  res.send('hello, world!');
});

To access morgan's documentation, please visit its page on npm.

From the log access perspective, only authorized individuals should have access to the logs.

Developers should also make sure that a mechanism which allows log analysis is set in place, also to guarantee that no untrusted data will be executed as code in the intended log viewing software or interface.

One of the most popular open source tools for this is the ELK stack: Elasticsearch, Logstash and Kibana. More information regarding this can be found here.

As a final step to guarantee log validity and integrity, a cryptographic hash function should be used as an additional step to ensure no log tampering has taken place.

The following example shows a working example of log file signing and validating, using SHA256 and the crypto package.

There are three parts to this algorithm:

  • Computing the digest
  • Signing the digest
  • Verifying the signature

In the first part of our program, we'll make use of the crypto module to compute the digest:

const crypto = require('crypto');
const fs = requre('fs');
const path = require('path');

const hasher = crypto.createHash('sha256');

const pathname = path.resolve(__dirname, 'logs', 'logfile.log');
const rs = fs.createReadStream(pathname);

rs.on('data', data => hasher.update(data));

rs.on('end', () => {
  // Part 2 - See below
});

In the second part, we'll make use of the crypto module to compute the digest and sign.

const digest = hasher.digest('hex');
const privKey = fs.readFileSync('private_key.pem');
const signer = crypto.createSign('RSA-SHA256');

signer.update(digest);

const signature = signer.sign(privKey, 'base64');

And finally, when we need to verify our signature, we can decrypt the signature and compare the result to the digest of the file as shown below:

const pubKey = fs.readFileSync('public_key.pem');
const verifier = crypto.createVerify('RSA-SHA256');
const testSig = verifier.verify(pubKey, signature, 'base64');
const verified = testSig === digest;

results matching ""

    No results matching ""