A spreadsheet is not an inert file
TechnoVision
Publication date pending · 5 min read
Every business application exports to CSV. Almost none of them treat that export as untrusted output. It is.
When Excel opens a file and a cell begins with an equals sign, it treats the contents as a formula and evaluates it. Same for a plus, a minus, and an at sign. This is documented behaviour, working as designed, and it has been true for decades.
The attack is boring, which is why it works
Somebody creates a customer, or a product, or a support ticket, with a name beginning with an equals sign. Your application stores it faithfully. Weeks later a colleague exports a report and opens it, and their spreadsheet evaluates whatever was in that field.
The interesting part is who runs it. Not the attacker - a member of staff, on a corporate laptop, in a trusted application, from a file that came from an internal system. Every signal a person uses to decide whether something is safe points the wrong way.
The export inherits the trust of the system that produced it, and none of the scrutiny.
What it can do
Modern Excel guards the worst of it behind warnings, and the guards have historically been bypassable. Even with them working, a formula can pull a remote resource, which turns a spreadsheet into a beacon that reports when and where the file was opened. That is enough to matter on its own.
The fix is one line and nobody writes it
Prefix any text cell whose first character is one of the dangerous ones with an apostrophe. Excel treats the rest as literal text; the apostrophe is invisible in the cell. That is the whole mitigation.
FORMULA_LEAD = ('=', '+', '-', '@', '\t', '\r')
def defuse(value):
"""Make a string safe to put in a spreadsheet cell."""
if isinstance(value, str) and value[:1] in FORMULA_LEAD:
return "'" + value
return valueDo not sanitise the numbers too
While you are in there
- Write a byte-order mark on UTF-8 CSV. The file is correct without it; Excel is not, and every accented name arrives mangled.
- Sanitise sheet names. Excel refuses a slash, a backslash, a colon, an asterisk, a question mark and square brackets, and caps the name at 31 characters. A report titled after a customer will eventually contain one of those.
- Write numbers as numbers and dates as dates. A column of text that looks numeric is a support ticket waiting to happen.
Why this belongs in the exporter
It is tempting to argue this is the spreadsheet’s problem, or the user’s. But the exporter is the only place in the chain that knows the value is data rather than a formula, and it is the only place the fix costs nothing.
It is the same argument as escaping HTML. Nobody thinks the browser should guess.