per2jensen/scrubexif
GitHub: per2jensen/scrubexif
基于 Docker 的 JPEG 照片 EXIF 元数据清理工具,在移除敏感位置和设备信息的同时保留摄影技术参数。
Stars: 1 | Forks: 0
# scrubexif
**GitHub**: [per2jensen/scrubexif](https://github.com/per2jensen/scrubexif)
**Docker Hub**: [per2jensen/scrubexif](https://hub.docker.com/r/per2jensen/scrubexif)
**高可信度 JPEG 清理。** 移除位置、序列号和私有相机标签,同时保留摄影上下文。使用最优秀的 [Exiftool](https://exiftool.org/) 来处理 JPEG。
**完整文档已移至** → [`DETAILS.md`](https://github.com/per2jensen/scrubexif/blob/main/doc/DETAILS.md)
本 README 为便于 Docker Hub 展示而有意保持简短。
## 快速开始
### 最简单的单行命令(默认安全模式,非破坏性)
清理 **当前目录**(`$PWD`)中的所有 JPEG,并将清理后的副本写入
`$PWD/output/`:
```
docker run --rm -v "$PWD:/photos" per2jensen/scrubexif:0.7.14
```
若要将清理后的文件写入其他目录(如不存在则会创建):
```
docker run --rm -v "$PWD:/photos" per2jensen/scrubexif:0.7.14 -o /photos/scrubbed
```
此操作将:
- 扫描 **您的当前目录**(`$PWD`)中的 `*.jpg` / `*.jpeg`(包括大写扩展名)
- 将清理后的副本写入 **$PWD/output/**(或自定义的 `--output` 目录)
- 原始文件在 **$PWD/** 中保持不变
- 若 `$PWD/output` 目录已存在,则拒绝运行
- 默认打印主机路径(使用 `--show-container-paths` 可包含 `/photos/...` 路径)
### 故障处理(重要)
scrubexif 的设计原则是 **绝不将未清理的 JPEG 放入输出目录**。
如果因任何原因导致清理失败,**该 JPEG 不会创建输出文件**,程序将继续处理其余文件。
清理失败时的处理方式取决于 `scrubexif` 的运行模式:
- **默认安全模式**(单行命令):失败的文件保留在原始目录中,且 **不会为这些失败文件写入任何文件到输出目录**。
- **自动模式**(`--from-input`):失败的文件被移动到 `processed/` 以供检查,且 **不会为这些失败文件写入 `output/`**。
- **手动(破坏性)原地清理**(`--clean-inline`):失败时原始文件保持不变;此模式不涉及输出目录。
### 加固(破坏性)原地清理(当前目录)
思路相同,但启用了容器加固和原地(破坏性)覆盖:
```
docker run -it --rm \
--read-only --security-opt no-new-privileges \
--tmpfs /tmp \
-v "$PWD:/photos" \
per2jensen/scrubexif:0.7.14 --clean-inline
```
### 批量工作流(PhotoPrism / 导入风格)
使用自动模式,并显式指定 input/output/processed 目录:
```
mkdir input scrubbed processed errors
docker run -it --rm \
--read-only --security-opt no-new-privileges \
--tmpfs /tmp \
-v "$PWD/input:/photos/input" \
-v "$PWD/scrubbed:/photos/output" \
-v "$PWD/processed:/photos/processed" \
-v "$PWD/errors:/photos/errors" \
per2jensen/scrubexif:0.7.14 --from-input
```
以下是您文件系统上使用的物理目录:
上传 → `$PWD/input/`
已清理 → `$PWD/scrubbed/`
原始文件 → `$PWD/processed/`(或使用 `--delete-original` 删除)
重复文件 → 默认删除;使用 `--on-duplicate move` 将其移动到 `$PWD/errors/`
清理失败(例如损坏的文件)→ 记录为失败;原始文件被移动到 `$PWD/processed/` 以供检查
`errors/` 此名称目前并不准确;它仅在设置 `--on-duplicate move` 时用于存放重复文件。将在后续版本中修复。
### 数据流概览(自动模式:`--from-input`)
此流程图描述 **仅在自动模式**(`--from-input`)下发生的情况,
该模式使用四个目录(`input/`、`output/`、`processed/`、`errors/`)。
请注意,这些是 **容器内部** 的目录名称。您文件系统上的物理目录是在运行 `docker run ...` 命令时映射的。请参见上方示例中的 `-v ....` 选项。
```
[input/] --> runs --> [output/]
|
+--> [processed/] (original JPEGs moved here after successful scrub,
unless --delete-original is used)
|
+--> [errors/] (duplicates only — only used when
--on-duplicate move)
```
含义:
- `input/`
新 JPEG 到达此处(例如来自上传,如 PhotoSync)。
- `output/`
带有安全 EXIF 元数据的已清理 JPEG。
- `processed/`
清理后移动到此处的原始 JPEG(或根据请求删除)。
- `errors/`
仅在启用 `--on-duplicate move` 时创建/使用。
### 本地构建与运行
```
# 从本仓库中的 Dockerfile 构建镜像
docker build -t scrubexif:local .
# 显示 CLI 用法 (ENTRYPOINT 运行 python -m scrubexif.scrub)
docker run --rm scrubexif:local --help
# 使用 hardened defaults 清理当前目录
docker run -it --rm \
--read-only --security-opt no-new-privileges \
--tmpfs /tmp \
-v "$PWD:/photos" \
scrubexif:local
```
任何追加到 `docker run … scrubexif:*` 的参数都会传递给底层的
`python3 -m scrubexif.scrub` 入口点。
## 主要特性
- 基于允许列表的清理:保留少量技术标签(曝光、ISO、焦距、方向、图像尺寸)
- 移除 GPS、序列号和其他私有元数据
- 默认保留色彩配置文件(使用 `--paranoia` 移除 ICC 数据)
- 自动模式支持重复文件处理(`--on-duplicate delete|move`)
- 针对热上传目录的可选稳定性检测(`--stable-seconds`、`--state-file`)
- 元数据检查和 dry-run 支持(`--show-tags`、`--preview`、`--dry-run`)
- 可选将版权和注释写入 EXIF/XMP(`--copyright`、`--comment`)
- 示例中默认启用加固容器配置(只读 + no-new-privileges)
## 供应链透明度
- 版本由公开的 **Release** GitHub Actions workflow(`.github/workflows/release.yml`)生成,该流程构建 Docker 镜像、运行 Syft 生成 SPDX SBOM,并使用 Grype 扫描镜像(遇到高/严重级别 CVE 时失败)。CI(`.github/workflows/CI.yml`)仅运行测试。
- Release 资产包括 SBOM(`sbom-v.spdx.json`)和 Grype SARIF 报告(`grype-results-.sarif`)。SARIF 也会上传到 GitHub Security 标签页并作为 Actions artifact 保留 → 请参阅 **[Releases 标签页](https://github.com/per2jensen/scrubexif/releases)** 查看 release 资产。
- `doc/build-history.json` 跟踪每个 tag 及其 Git commit、镜像摘要以及(可获取时的)Grype 严重级别统计,为下游用户提供可验证的审计追踪。
## 常用选项
```
--from-input auto mode
--clean-inline in-place scrub (destructive)
--show-container-paths include container paths in output
-q, --quiet no output on success
--preview no write, view only
--paranoia maximum scrub, removes ICC
--comment stamp comment into EXIF/XMP
--copyright stamp copyright into EXIF/XMP
--on-duplicate delete | move
--stable-seconds N intake stability window
--state-file PATH override queue DB
-o, --output DIR write scrubbed files to DIR (default safe mode)
```
完整 CLI 参考 → 见 [`DETAILS.md`](https://github.com/per2jensen/scrubexif/blob/main/doc/DETAILS.md)
## 示例设置
这是我快速将 JPEG 文件上传到 PhotoPrism 的工作流示例。
一个用例是在展览现场快速向狗主人展示照片。
| 主机文件系统路径 | 容器路径 | 用途 |
| ------------------------ | -------------------- | ------------------------------------------------ |
| `/some/directory/` | `/photos/input/` | 服务器上用于新 JPEG 上传的位置 |
| `/photoprism/sooc/` | `/photos/output/` | 清理后的 JPEG 版本的目标位置,用于 PhotoPrism 导入|
| `/photoprism/processed/` | `/photos/processed/` | 已导入文件的暂存区。 |
### Systemd
`/etc/systemd/system/scrubexif.service`:
```
[Service]
ExecStart=/usr/bin/docker run --rm \
--read-only --security-opt no-new-privileges \
--tmpfs /tmp \
-v /some/directory:/photos/input \
-v /photoprism/sooc:/photos/output \
-v /photoprism/processed:/photos/processed \
per2jensen/scrubexif:0.7.14 --from-input --stable-seconds 10
```
`/etc/systemd/system/scrubexif.timer`:
```
[Unit]
Description=Run scrubexif every 5 minutes
[Timer]
OnBootSec=1min
OnUnitActiveSec=5min
Persistent=true
[Install]
WantedBy=timers.target
```
### Photoprism systemd 脚本
我使用 `scrubexif` 来清理我在犬展上的 JPEG 照片。我使用 rclone 将文件上传到服务器,systemd timer 每 5 分钟运行一次下面的脚本。
您可以在 [Github scrubexif 仓库](https://github.com/per2jensen/scrubexif/blob/main/scripts/run_scrubexif_photoprism.sh) 中查看我的(已匿名化)脚本。
## 开发
```
make dev-clean # remove dev image
make test # make dev image and run full test suite
pytest -m soak # optional 10 min run or try scripts/soak.sh
```
## 许可证
GPL-3.0-or-later
根据 GNU GENERAL PUBLIC LICENSE v3 授权,详情见提供的 "LICENSE" 文件。
在适用法律允许的范围内,本程序不提供任何担保,即使是针对特定用途的适销性或适用性也不提供担保。
请参阅提供的 "LICENSE" 文件中的第 15 节和第 16 节。
**GitHub**: [per2jensen/scrubexif](https://github.com/per2jensen/scrubexif)
**Docker Hub**: [per2jensen/scrubexif](https://hub.docker.com/r/per2jensen/scrubexif)
完整文档 → [`DETAILS.md`](https://github.com/per2jensen/scrubexif/blob/main/doc/DETAILS.md)
标签:Docker, ExifTool, EXIF清洗, JPEG, NIDS, 元数据移除, 图片处理, 地理定位移除, 安全防御评估, 容器化, 开源安全工具, 敏感信息清理, 数据脱敏, 照片发布, 网络安全, 自动化处理, 请求拦截, 逆向工具, 逆向工程平台, 隐私保护