Formula Injection in Express
How Formula Injection Manifests in Express
Formula Injection in Express applications occurs when user-controlled data is embedded in spreadsheet exports without proper sanitization. This vulnerability allows attackers to inject malicious formulas (like =1+1 or =DANGEROUS()) that execute when the spreadsheet is opened in applications like Microsoft Excel or Google Sheets.
In Express, this typically happens in three scenarios:
- CSV export endpoints where user data is directly written to CSV files
- Excel/Spreadsheet generation using libraries like
xlsxorexceljs - Financial reporting where calculation cells might reference user data
The most common attack pattern involves an attacker submitting data containing formula syntax through your API. For example:
// Vulnerable Express endpoint
app.post('/export-csv', (req, res) => {
const users = await db.getUsers();
// User data flows directly to CSV without sanitization
const csv = 'Name,Email,Balance\n';
users.forEach(user => {
csv += `${user.name},${user.email},${user.balance}\n`;
});
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename=data.csv');
res.send(csv);
});If user.name contains =1+1, the resulting CSV will execute this formula when opened in Excel, potentially leading to:
- Information disclosure through
=GET.WORKBOOK()or=EXTERNAL()functions - Remote code execution via
=EXEC()or macro-enabled formulas - Denial of service through infinite loops or resource exhaustion
- Data exfiltration using network functions like
=WEBSERVICE()
Express applications are particularly vulnerable because they often serve as the backend for web applications that generate reports, exports, and analytics dashboards where user data is frequently exported to spreadsheets.
Express-Specific Detection
Detecting Formula Injection in Express requires both static analysis and runtime scanning. Here's how to identify this vulnerability:
Static Code Analysis
Search your Express codebase for these patterns:
# Look for CSV generation patterns
grep -r 'Content-Type.*csv' routes/ || grep -r 'application/csv' routes/
grep -r 'Content-Disposition.*csv' routes/
# Find Excel generation libraries
grep -r 'xlsx' routes/ || grep -r 'exceljs' routes/ || grep -r 'csv-writer' routes/
# Check for direct string concatenation in exports
grep -r '\.send(' routes/ | grep -E '(csv|excel|xlsx)'Focus on endpoints that:
- Generate downloadable files (CSV, XLSX, XLS)
- Export user-generated content
- Handle financial or reporting data
- Don't sanitize user inputs before file generation
Runtime Scanning with middleBrick
middleBrick's black-box scanning approach is particularly effective for detecting Formula Injection in Express APIs. The scanner tests unauthenticated endpoints by submitting formula payloads and analyzing responses.
To scan your Express API:
npm install -g middlebrick
middlebrick scan https://yourapi.com/export-csv
middleBrick tests for Formula Injection by:
- Injecting formula payloads like
=1+1,=TRUE+TRUE,=GET.WORKBOOK() - Checking if the response contains formula syntax
- Analyzing if the exported file executes formulas when opened
- Testing for SSRF via
=WEBSERVICE()formulas
The scanner provides a security score (A–F) and specific findings with remediation guidance. For Formula Injection, you'll see:
- Risk severity (Critical/High)
- Affected endpoints
- Specific formula payloads that triggered the vulnerability
- Recommended fixes with code examples
middleBrick's advantage is that it tests the actual runtime behavior without requiring source code access, making it ideal for Express applications in production or staging environments.
Express-Specific Remediation
Remediating Formula Injection in Express applications requires input sanitization and safe CSV generation. Here are Express-specific solutions:
Input Sanitization Middleware
Create a middleware that sanitizes formula characters before processing:
const formulaSanitizer = (req, res, next) => {
const formulaChars = ['=', '+', '-', '@', '(', ')', '[', ']', '{', '}'];
const sanitize = (obj) => {
if (typeof obj === 'string') {
// Prepend apostrophe to formula-starting strings
if (formulaChars.some(char => obj.startsWith(char))) {
return `'’${obj}`;
}
return obj;
} else if (Array.isArray(obj)) {
return obj.map(sanitize);
} else if (typeof obj === 'object' && obj !== null) {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => [k, sanitize(v)])
);
}
return obj;
};
req.body = sanitize(req.body);
req.query = sanitize(req.query);
next();
};
// Apply to specific routes
app.post('/export-csv', formulaSanitizer, (req, res) => {
// Safe export logic
});Safe CSV Generation with csv-writer
Use the csv-writer library with proper escaping:
const createCsvWriter = require('csv-writer').createObjectCsvWriter;
app.get('/safe-export', async (req, res) => {
const users = await db.getUsers();
// Sanitize data before export
const sanitizedUsers = users.map(user => ({
name: `'’${user.name.replace(/^=/, '')}`,
email: user.email,
balance: user.balance
}));
const csvWriter = createCsvWriter({
path: 'output.csv',
header: [
{id: 'name', title: 'Name'},
{id: 'email', title: 'Email'},
{id: 'balance', title: 'Balance'}
]
});
await csvWriter.writeRecords(sanitizedUsers);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', 'attachment; filename=data.csv');
res.sendFile('output.csv', { root: __dirname });
});Excel Generation with Formula Protection
When using Excel libraries, explicitly disable formula execution:
const ExcelJS = require('exceljs');
app.get('/excel-export', async (req, res) => {
const users = await db.getUsers();
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Users');
// Add headers
worksheet.columns = [
{ header: 'Name', key: 'name', width: 30 },
{ header: 'Email', key: 'email', width: 40 },
{ header: 'Balance', key: 'balance', width: 15 }
];
// Add data with formula protection
users.forEach((user, index) => {
const row = worksheet.getRow(index + 2);
row.values = [
`'’${user.name.replace(/^=/, '')}`,
user.email,
user.balance
];
// Set text format to prevent formula execution
row.eachCell((cell, colNumber) => {
if (colNumber === 1) { // Name column
cell.numFmt = '@'; // Text format
}
});
});
const buffer = await workbook.xlsx.writeBuffer();
res.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
res.setHeader('Content-Disposition', 'attachment; filename=data.xlsx');
res.send(buffer);
});Comprehensive Protection Strategy
Implement a defense-in-depth approach:
- Sanitize all user inputs that might appear in exports
- Use libraries that automatically escape formula characters
- Set appropriate content-type headers
- Validate data types before export
- Regularly scan with middleBrick to detect regressions
middleBrick's continuous monitoring (Pro plan) can automatically scan your Express API endpoints on a schedule, alerting you if Formula Injection vulnerabilities appear in new code or updated dependencies.
Frequently Asked Questions
How does Formula Injection differ from XSS in Express applications?
=FUNCTION(), whereas XSS uses HTML/JS injection. Both require input sanitization, but Formula Injection needs spreadsheet-aware escaping like prepending apostrophes to formula-starting strings.Can middleBrick detect Formula Injection in my Express API?
=WEBSERVICE() formulas, and provides a security score with specific findings and remediation guidance. The scan takes 5–15 seconds and requires no credentials or setup.