照片删了 EXIF 就安全了?一张图里的信息其实有 4 层
上周三下午,一个做二手家具的朋友发我张照片,问我能不能看出是哪个小区拍的。她特意强调「EXIF 我全删了」。我把图拖进 exiftool 一看,确实干净,只剩下 ColorSpace 和 Orientation 两个字段。然后我放大到 200%,在窗户玻璃的反光里找到一辆车的车牌,又在她身后便利店的灯箱上读到「XX 生鲜 24 小时」。把这两条丢进地图,圈出三个候选小区。整个过程,我没用上任何一条 EXIF。
这事之后我重新捋了一遍「图片信息」这个概念。绝大多数人把它等同于 EXIF,删掉就以为匿名了。实际上能从一个 JPEG 文件里读出来的东西至少分四层,从外到内是:文件层、元数据层、像素层、语义层。越往里越难删,也越容易在「删」的过程中把别的东西一起搞坏。
第 1 层 文件层:不打开图就能读到的东西
文件名、扩展名、文件大小、创建和修改时间、MD5 / SHA256 哈希,这些不需要解码图像就能拿到。
文件大小本身很有信息量。一台 iPhone 15 Pro 拍出来的 48MP HEIC 大概 4–6MB,同一张图转成 JPEG 质量 85 会膨胀到 8–12MB;反过来,一张号称「原图」的 4000×3000 JPEG 如果只有 800KB,基本可以断定被重压过。修改时间更直白——很多人删完元数据忘了改时间戳,导出时间比拍摄时间晚三年,这个矛盾本身就能说明问题。
还有个冷门点:文件名的残留。相机直出是 IMG_20230812_143022.jpg,微信保存是 mmexport1691823xxxxx.jpg,某些网盘同步还会带设备别名。这些字符串经常被当成噪音忽略掉。
第 2 层 元数据层:EXIF / XMP / IPTC / ICC
这是大家最熟的一层,也是最容易被一刀切删掉的一层。结构上它藏在 JPEG 的 APP1 段(标记 0xFFE1)里,常见的几组:
- 设备组:0x010F Make、0x0110 Model,直接写着 Apple iPhone 14 Pro
- 时间组:0x0132 DateTime、0x9003 DateTimeOriginal,精度到秒,通常带时区偏移
- 曝光组:0x829A ExposureTime(1/120)、0x829D FNumber(1.78)、0x8827 ISOSpeedRatings(320)、0x920A FocalLength(6.86mm)
- 方向组:0x0112 Orientation,取值 1–8,这就是「竖着拍横着看」的根因
- GPS IFD:0x0001–0x0006,经纬度用度分秒三个有理数存储。城市里实测精度 10–30 米,郊区开阔地能到 5 米
- XMP:Adobe 系软件写入,包含编辑历史、原始文件名,甚至 Lightroom 的调整参数
- IPTC:版权、作者、标题,媒体行业用得多
- ICC Profile:sRGB IEC61966-2.1 大约 3KB,Adobe RGB 能到 500KB 以上
一组实测数据给你个量级感:iPhone 直出的照片用 exiftool 数大概 60–80 个标签;从微信「保存到相册」拿回来的,通常只剩 2–3 个(ColorSpace、Orientation 之类)。小红书压得更狠,长边限制到 1080,元数据基本清空。
但这里有个坑:很多工具只删主图的 EXIF,不删 APP1 里嵌的那张缩略图。 JPEG 的 EXIF 规范允许在 IFD1 里塞一张 160×120 的缩略图,它有独立的压缩数据流。有些在线「一键去 EXIF」处理完,用 exiftool -a -u 还是能把缩略图抠出来。我 2022 年踩过一次,客户发来的「已脱敏」图,主图 GPS 没了,缩略图里的坐标原封不动。
第 3 层 像素层:删不掉的那部分,也是最容易被忽略的一层
把元数据全删光,像素层的信息一点都不会少。
第一类是编码指纹。 JPEG 的量化表(DQT 段,0xFFDB)和哈夫曼表是按编码器写死的。相机直出、Lightroom 导出、Photoshop「存储为 Web 格式」,这三者生成的量化表长得完全不一样。有人拿公开的量化表库做匹配,能反推图是用什么软件、什么质量参数压出来的,准确率不低。这跟水印是两回事——它在文件结构里,不在画面上。
第二类是传感器噪声。 PRNU(光响应非均匀性)是 CMOS 制造过程留下的固定噪声模式,近似指纹。几十张同设备拍的图做平均就能把指纹提出来,然后判断某张图是不是同一台机器拍的。这是司法取证里的常规手段,不是科幻。
第三类是修改痕迹。 ELA(误差水平分析)的原理是:同一张 JPEG 里,没动过的区域和 P 过的区域经历过不同次数的压缩,重新保存一遍后各区域的误差水平会拉开差距。肉眼看是均匀的一面墙,ELA 图上会浮出一个方框。
第四类是拍摄方式的痕迹。 屏幕翻拍会有摩尔纹和隐约的像素栅格;截图有锐利的文字边缘但没有镜头畸变;多帧合成的夜景照片放大后能看到轻微鬼影。这些都是「看一眼就知道」的层级,可很多人压根没意识到自己发的是翻拍件。
说句实在的:靠删元数据来实现匿名,等于把身份证撕了但脸还露着。
第 4 层 语义层:图里「长」出来的信息
这层不靠技术读,靠看。
场景里的路牌、门牌号、店招、公交站牌、窗外地形轮廓;人物身上的校徽、工牌、制服;镜子、玻璃、金属表面、手机屏幕反光,甚至瞳孔里反射的影像(这个真有人做,有专门的论文做眼反演);桌角一张没注意到的快递单。
再叠加以图搜图这条路径。Google Lens 和 Yandex 对场景匹配比较强,TinEye 偏向找完全相同或轻微改动的副本。pHash 这类感知哈希是 64 位的,缩放、调亮度、加水印之后,哈希值的汉明距离可能只差几位,照样能匹配上。
如果你发的是带 alt 文本或周边配文的图,语义层的信息量还要翻一倍——图片本身什么线索都没有,但配文里一句「我们小区」就把范围缩到几百米以内了。
怎么查:几行命令把前三层看一遍
Windows 上右键 → 属性 → 详细信息,只能看到几十个常用字段,GPS 有没有都显示不全。macOS 在访达里选中文件按 Cmd+I 看简介,或者在「预览」里按 Cmd+I 打开显示检查器,能看到的多一点,但也只覆盖第二层。
命令行才是完整的:
# -a 显示重复标签,-u 显示未知标签,-g1 按组分层
# 输出很长,建议重定向到文件再看
exiftool -a -u -g1 photo.jpg > meta.txt

# 单独把 GPS 和缩略图抠出来
# 有输出 = 有坐标,没输出 = 干净
exiftool -gps:all -b -ThumbnailImage photo.jpg > thumb.jpg
# ImageMagick 检查有没有嵌 ICC、是不是 CMYK
identify -verbose photo.jpg | grep -i profile
# 不看内容,只留指纹和大小
sha256sum photo.jpg && ls -l photo.jpg
手机上也有一条捷径:iOS 的照片 App 里把图往上滑,如果里面有 GPS,会直接给你一张地图缩略图。这是最直观的体检。
怎么删:按目的选工具,别一键梭哈
最省事但后果也最重的命令是 exiftool -all= photo.jpg。它会删掉所有可写标签,包括 ICC 色彩描述文件。删完 ICC 之后,某些看图软件会拿 sRGB 硬套,原来在广色域里调过的图会发灰、发暗。我一般这么做:
cp photo.jpg photo.bak.jpg # 先备份,别嫌麻烦
exiftool -all= photo.jpg # 全删
# 把颜色配置从备份里拷回来,否则可能出现色偏
exiftool -tagsfromfile photo.bak.jpg -icc_profile -ColorSpaceTags photo.jpg
exiftool -a -u -g1 photo.jpg # 复查:不该有 GPS、序列号、缩略图
Python 侧,piexif 只能处理 EXIF,XMP 和 IPTC 它管不着,别指望一条 piexif.remove() 走天下:
import piexif
# 只删 EXIF,XMP / IPTC / ICC 都还在
piexif.remove('photo.jpg')
真要彻底,我自己的顺序是:先裁掉画面边缘 3%–5%(顺手把角落的水印和部分边缘线索去掉),再重新导出成 PNG,最后按需要转 WebP 或 JPEG。重新编码会破坏原本的量化表和 PRNU 指纹,代价是画质掉一档。要求更高的话用另一台设备翻拍自己的屏幕,但注意别拍出摩尔纹——那反而又暴露了拍摄方式。
最后说个我自己的判断线
判断一张图能不能发,我现在只问三个问题:
- 别人只靠画面内容,能不能把地点缩到 1 公里以内?
- 画面里有没有任何一处的反光、镜子、玻璃、亮面屏幕?
- 发出去之后,会不会被相似图检索翻出旧版本?
三个都过,才发原图。有一个过不了,就裁、重压,或者干脆换一张。删 EXIF 是必要动作,但它只是四层里的第二层。把它当成全部,迟早要吃亏。