Writing high-quality code is crucial for any successful software project. To achieve this, developers must follow best practices that ensure security, efficiency, maintainability, TDD, and documentation throughout the development process.
To ensure secure software product development, we need to follow best practices to avoid security vulnerabilities. Below are the guidelines that we follow.
Sanitize all user inputs by removing HTML and JS from it, thus avoiding XSS vulnerability. This sanitization has to be done in a deep manner, meaning:
Following is an example snippet from our open-source project showing how to sanitize recursively, in a deep manner:
const sanitizeHtml = require('sanitize-html');
class SanitizeRecursively {
/**
* Recursively sanitize.
*
* @param {object} params - The input parameters to be sanitized
*
* @returns {*} The sanitized output
*/
sanitizeParamsRecursively(params) {
const oThis = this;
if (typeof params === 'string') {
// For string, sanitize it by using 'sanitize-html' npm
params = oThis._sanitizeString(params);
} else if (typeof params === 'boolean' || typeof params === 'number' || params === null) {
// Do nothing and return param as is.
} else if (params instanceof Array) {
// For an array, iterate over each element and recursively sanitize it
for (const index in params) {
params[index] = oThis.sanitizeParamsRecursively(params[index]);
}
} else if (params instanceof Object) {
// For an object, iterate over each value and recursively sanitize it
Object.keys(params).forEach(function(key) {
params[key] = oThis.sanitizeParamsRecursively(params[key]);
});
} else if (!params) {
// Check if the input parameter is falsy (null, undefined, 0, false, NaN, '')
// If yes, sanitize it as a string
params = oThis._sanitizeString(params);
} else {
console.error('Invalid params type: ', typeof params);
params = '';
}
return params;
}
/**
* Sanitize string
*
* @param str - The string to be sanitized
*
* @private
*/
_sanitizeString(str) {
const sanitizedHtmlStr = sanitizeHtml(str, { allowedTags: [] });
return sanitizedHtmlStr.replace(/javascript:/g, '');
}
}
User input can be present in query parameters, request body, route parameters and headers (including cookies). Parameter sanitization should be done before using any of them them inside code.
Refer to our blog on XSS vulnerability to know more on how to prevent it.
Refer our open-source project CommonValidator class for examples.
Avoid using the user inputs directly in the query as it can result in a query injection vulnerability.
Use parameterized queries to avoid this vulnerability and separate the query logic from the user input.
Following is an example code snippet for Node.js using the mysql npm:
// Sample code for parameterized queries
const userId = 1;
connection.query('SELECT * FROM users WHERE id = ?', [userId], (error, results, fields) => {
if (error) throw error;
console.log(results);
});