browserforensics
Back to all articles

The Safari Cookies.binarycookies format

2026-06-21 · 3 min

Chrome and Firefox keep cookies in SQLite, so most tooling assumes a database. Safari does not — it writes a single packed binary file, and the values are cleartext rather than encrypted. The format is small enough to walk by hand, which is exactly what you want when no parser is at reach during triage.

Where it lives

On modern macOS the file is sandboxed under the Safari container:

~/Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies

Older builds kept it at ~/Library/Cookies/Cookies.binarycookies. If you inherit a logical acquisition, search both paths — the container copy is the live one.

File layout

Everything outside the cookie records is big-endian. The overall shape:

OffsetFieldNotes
0Magic cook4 bytes, ASCII
4Page countbig-endian uint32
8Page sizesbig-endian uint32 each, one per page
...Page blocksback to back, sizes as declared above

So you read the magic, read how many pages exist, read that many 4-byte sizes, then the pages follow in order. Knowing each page size up front lets you carve pages without parsing their internals first.

Page structure

Each page is self-describing:

FieldNotes
Page headerleading tag bytes
Cookie counthow many records on this page
Cookie offsetsone per cookie, relative to the page start
Cookie recordsthe records themselves

The per-cookie offsets are the key to random access: jump to page start + offset and you are at the top of a record without scanning.

Cookie record

Each record opens with a fixed header (roughly 56 bytes) carrying the record size, a flags bitfield, and the byte offsets of the four strings that follow. After the header come four NUL-terminated strings, in order:

  1. domain
  2. name
  3. path
  4. value

Because the header stores the offset of each string, you do not guess boundaries — you seek. The flags bitfield encodes the usual cookie attributes:

FlagMeaning
0x1Secure
0x4HTTPOnly

The exact bit set varies across Safari versions; treat unknown bits as informational rather than authoritative.

The values matter here. Where Chromium encrypts encrypted_value at rest, Safari stores the cookie value in the clear — a session token or OAuth state sits right there in the record, no keychain required.

Timestamps

Two timestamps live in the record header: creation and expiry. Both are 8-byte doubles (float64), not integers, and both are Mac absolute time — seconds since 2001-01-01 00:00:00 UTC.

unix_seconds = mac_absolute_time + 978307200

This is the same epoch Safari's History.db uses, so cookie and history timelines align without a second conversion.

One trap: an expiry of 0 means a session cookie, not the year 2001. Render it as "session" rather than feeding 0 through the epoch math and emitting 2001-01-01.

Parsing it by hand

A minimal walk, no SQLite involved:

  1. Confirm magic cook.
  2. Read the big-endian page count, then that many page sizes.
  3. For each page, read the cookie count and the per-cookie offsets.
  4. At each offset, read the header, pull the four string offsets and the two doubles, then slice out domain/name/path/value.
  5. Convert both doubles with + 978307200; map 0 expiry to session.

That is the entire format. The BrowserForensics parser does exactly this in the browser — no upload, no database engine.

Further reading