Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Support for insecure settings i.e crypto, hash and random #185

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 124 additions & 0 deletions lib/instrumentation-security/hooks/crypto/nr-crypto.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* Copyright 2023 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: New Relic Software License v1.0
*/

module.exports = initialize
const requestManager = require('../../core/request-manager');
const secUtils = require("../../core/sec-utils");
const API = require("../../../nr-security-api");
const securityMetaData = require('../../core/security-metadata');
const { EVENT_TYPE, EVENT_CATEGORY } = require('../../core/event-constants');
const { NR_CSEC_FUZZ_REQUEST_ID } = require('../../core/constants');
const logger = API.getLogger();

/**
* Entry point of ldapjs and ldapts module hook
* @param {*} shim
* @param {*} mod
* @param {*} moduleName
*/
function initialize(shim, mod, moduleName) {
logger.info('Instrumenting ' + moduleName)
cryptoCipherHooks(shim, mod, 'createCipheriv', moduleName);
cryptoHashHmacHooks(shim, mod, 'createHash', moduleName);
cryptoHashHmacHooks(shim, mod, 'createHmac', moduleName);
cryptoRandomHooks(shim, mod, 'randomInt', moduleName);
cryptoRandomHooks(shim, mod, 'randomBytes', moduleName);
cryptoRandomHooks(shim, Math, 'random', "Math");
}
/**
* wrapper to hook crypto cipher methods
* @param {*} shim
* @param {*} mod
* @param {*} methodName
* @param {*} moduleName
*/
function cryptoCipherHooks(shim, mod, methodName, moduleName) {
shim.wrap(mod, methodName, function makeWrapper(shim, fn) {
logger.debug(`Instrumenting ${moduleName}.${methodName}`);
return function wrapper() {
const interceptedArgs = [arguments[0]];
shim.interceptedArgs = interceptedArgs;
const request = requestManager.getRequest(shim);
if (request) {
const traceObject = secUtils.getTraceObject(shim);
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.CIPHER, EVENT_CATEGORY.CRYPTO)
const secEvent = API.generateSecEvent(secMetadata);
this.secEvent = secEvent;
API.sendEvent(secEvent);
}
const result = fn.apply(this, arguments);

if (result && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
API.generateExitEvent(this.secEvent);
delete this.secEvent
}
return result;
}
})
}


/**
* wrapper to hook crypto hash and mac methods
* @param {*} shim
* @param {*} mod
* @param {*} methodName
* @param {*} moduleName
*/
function cryptoHashHmacHooks(shim, mod, methodName, moduleName) {
shim.wrap(mod, methodName, function makeWrapper(shim, fn) {
logger.debug(`Instrumenting ${moduleName}.${methodName}`);
return function wrapper() {
const interceptedArgs = [arguments[0]];
shim.interceptedArgs = interceptedArgs;
const request = requestManager.getRequest(shim);
if (request) {
const traceObject = secUtils.getTraceObject(shim);
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.HASH, EVENT_CATEGORY.HASH)
const secEvent = API.generateSecEvent(secMetadata);
this.secEvent = secEvent;
API.sendEvent(secEvent);
}
const result = fn.apply(this, arguments);
if (result && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
API.generateExitEvent(this.secEvent);
delete this.secEvent
}
return result;
}
})
}
/**
* Wrapper for random hooks
* @param {*} shim
* @param {*} mod
* @param {*} methodName
* @param {*} moduleName
*/
function cryptoRandomHooks(shim, mod, methodName, moduleName) {
shim.wrap(mod, methodName, function makeWrapper(shim, fn) {
logger.debug(`Instrumenting ${moduleName}.${methodName}`);
return function wrapper() {
const interceptedArgs = [arguments[0]];
shim.interceptedArgs = interceptedArgs;
const request = requestManager.getRequest(shim);
if (request && arguments[0]) {
const traceObject = secUtils.getTraceObject(shim);
const secMetadata = securityMetaData.getSecurityMetaData(request, interceptedArgs, traceObject, secUtils.getExecutionId(), EVENT_TYPE.WEAKRANDOM, EVENT_CATEGORY.WEAKRANDOM)
const secEvent = API.generateSecEvent(secMetadata);
this.secEvent = secEvent;
API.sendEvent(secEvent);
}
const result = fn.apply(this, arguments);
if (result && request && request.headers[NR_CSEC_FUZZ_REQUEST_ID]) {
API.generateExitEvent(this.secEvent);
delete this.secEvent
}
return result;
}
})
}


9 changes: 9 additions & 0 deletions lib/instrumentation-security/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,15 @@ newrelic.instrumentWebframework({
})


newrelic.instrument({
moduleName: 'crypto',
onRequire: require('./hooks/crypto/nr-crypto'),
onError: function intrumentErrorHandler(err) {
logger.error(err.message, err.stack)
}
})





Expand Down
95 changes: 95 additions & 0 deletions test/instrumentation-security/nr-crypto.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2023 New Relic Corporation. All rights reserved.
* SPDX-License-Identifier: New Relic Pre-Release
*/

'use strict'

const test = require('tap').test;
const utils = require('@newrelic/test-utilities');
const crypto = require('crypto');

test('crypto', (t) => {
t.autoend();
let helper = null;
let initialize = null;
let shim = null;

t.beforeEach(() => {
helper = utils.TestAgent.makeInstrumented()
shim = helper.getShim();
initialize = require('../../lib/instrumentation-security/hooks/crypto/nr-crypto');
initialize(shim, crypto, 'crypto');
})

t.afterEach(() => {
helper && helper.unload()
})

t.test('crypto.randomBytes', (t) => {
let x = crypto.randomBytes(16);
t.equal(true, x instanceof Buffer)
t.equal(16, shim.interceptedArgs[0])
t.end();
})

t.test('crypto.randomInt', (t) => {
let x = crypto.randomInt(11);
t.equal(true, Number.isInteger(x))
t.equal(11, shim.interceptedArgs[0])
t.end();
})

t.test('Math.random', (t) => {
let x = Math.random(21);
t.equal(false, isNaN(x))
t.equal(21, shim.interceptedArgs[0])
t.end();
})

t.test('crypto.createHash', (t) => {
const md5Hash = crypto.createHash('md5');
// Data to be hashed
const data = 'Hello, world!';
// Update the hash with the data
md5Hash.update(data);
// Obtain the hash digest in hexadecimal form
const hashResult = md5Hash.digest('hex');
t.equal(true, typeof hashResult === 'string')
t.equal('md5', shim.interceptedArgs[0])
t.end();
})

t.test('crypto.createHmac', (t) => {
// Secret key for HMAC
const secretKey = 'mySecretKey';
Fixed Show fixed Hide fixed
// Data to be hashed
const data = 'Hello, world!';
// Create an HMAC object with the SHA256 algorithm
const hmac = crypto.createHmac('sha256', secretKey);
// Update the HMAC object with the data
hmac.update(data);
// Obtain the HMAC digest in hexadecimal form
const hmacResult = hmac.digest('hex');
t.equal(true, typeof hmacResult === 'string')
t.equal('sha256', shim.interceptedArgs[0])
t.end();
})

t.test('crypto.createCipheriv', (t) => {
let text = 'Sumit Suthar';
const key = crypto.randomBytes(32);
const iv = crypto.randomBytes(16);
let cipher = crypto.createCipheriv('aes-256-cbc', Buffer.from(key), iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
let iv1 = iv.toString('hex');
let encryptedData = encrypted.toString('hex')

t.equal('aes-256-cbc', shim.interceptedArgs[0]);
t.equal(true, typeof encryptedData === 'string')
t.equal(false, typeof iv1 !== 'string')
t.end();
})

})
Loading