採用フォームにファイル添付を置くなら、セキュリティ(悪意ファイルの遮断)と個人情報保護(安全な保管・廃棄)を同時に満たす必要があります。本記事は、現場でそのまま使える受け取り→検査→保管→配布→廃棄の実装テンプレをまとめました。
前提:ファイル添付はなぜ危険か
- 拡張子偽装・MIME偽装で悪意あるスクリプトやマクロを混入できる
- アップロード後の直リンク公開やインライン表示で情報漏えい/XSSになる
- 履歴書には高密度の個人情報が含まれ、保存・共有・廃棄の全工程が法的/倫理的な配慮対象
「ファイルタイプは拡張子やContent-Typeだけに頼らず検証」「サイズ/ファイル名/保存先の制限」「アップロードは認証ユーザーのみ」— OWASP File Upload Cheat Sheet の基本原則。
参考:OWASP File Upload Cheat Sheet
安全設計の全体像(推奨フロー)
- 許可ルール:拡張子/サイズ/ページ数などの事前チェック
- アップロード:アプリ経由ではなくストレージ直送(プリサインドURL)
- 検査:到着直後にウイルス/マクロ検査、NGは隔離
- 保管:非公開バケット/フォルダ、暗号化+アクセス制御
- 配布:採用担当のみ期限付きダウンロード、常に添付配布はリンク経由
- 廃棄:選考終了・所定日数で自動削除、ログは法定範囲で保存
1) 許可ポリシー(拡張子/サイズ/名前)
- 拡張子は
.pdf
を推奨(Office系はマクロを起動しない方針)。どうしても受ける場合は.docx
/.xlsx
のみ - 最大サイズ:5〜10MB程度(GTM/GA4の送信にも影響しづらい)
- ファイル名はサーバ生成のランダム名に置換。原本名はDBにメタとして保存
- 拡張子ホワイトリスト+シグネチャ(マジックナンバー)検査を併用
「許可拡張子のホワイトリスト」「MIME/マジックナンバーの検査」「保存名の再生成」「サイズ上限」などはOWASP推奨。
参考:OWASP File Upload Cheat Sheet / Unrestricted File Upload
2) S3プリサインドURL直送(安全な受け取り)
アプリサーバにファイル本体を通さず、ストレージへ直接アップロードする方式にすると、帯域/可用性/権限分離の面で堅牢になります。
- 有効期限の短いプリサインドURLを発行(数分〜最長7日、用途に応じて)
- キーは応募ID/日付で名前空間分離、上書き禁止(ユニークキー)
- アップロード後は即スキャン→結果OKなら「確認済み」タグを付与
「プリサインドURLは時間制限と操作限定で管理」— AWS公式。
参考:S3 presigned URL(AWS Docs) / Best Practices(AWS Prescriptive Guidance)[PDF]
3) ウイルス/マクロ検査(到着直後に自動)
- ClamAV 等のエンジンでスキャン(更新シグネチャで定期アップデート)
- スキャンは非同期キューで実行、結果がNGなら隔離フォルダへ移動し担当に通知
- 合格ファイルのみ「
safe=true
」メタタグを付与、UIは「審査中/合格/隔離」を表示
ClamAVはデーモン(clamd)で高速スキャンとオンアクセススキャンを提供。
参考:ClamAV Scanning / On-Access Scanning
4) 非公開保管と暗号化・アクセス制御
- ストレージは完全非公開(公開URLなし)。ダウンロードは都度発行の期限付きリンクのみ
- 暗号化:サーバサイド暗号化(SSE-KMS等)+転送時TLS
- アクセスは採用担当ロールのみ、アクセスログ(誰がいつ何をDL)を保存
5) 廃棄ポリシー(保管期間・消去)
- 個人情報の保存期間は法律で一律に定まっていませんが、利用目的がなくなったら遅滞なく消去に努めるのが原則
- 第三者提供の「提供記録」は原則3年間保存(該当時)
- 採用実務では「選考終了から60〜180日で自動削除」等、明文化して同意を取得
「保存期間の規定はないが、必要がなくなれば遅滞なく消去に努める(法22条)」/「第三者提供の記録は原則3年保存」— 個人情報保護委員会FAQ。
参考:取得した個人情報は、いつ廃棄? / 第三者提供の記録保存(3年)
6) ダウンロード配布の安全策
- インライン表示を避け、
Content-Disposition: attachment
で常にダウンロード - ファイル名は面接ID_候補者イニシャル_日付.pdf等、過度な個人情報を入れない
- メール添付は原則禁止。期限付きリンクを共有(社外共有時はパス付き+閲覧期限)
7) 実装スニペット(要点)
サーバ側:MIME/シグネチャ検査(例:Node.js)
// pseudo: magic number check (PDFだけ許可)
import fs from 'node:fs/promises';
const PDF_SIG = Buffer.from([0x25,0x50,0x44,0x46]); // %PDF
async function isPdf(filePath){
const fd = await fs.open(filePath, 'r'); const b = Buffer.alloc(4);
await fd.read(b, 0, 4, 0); await fd.close();
return b.equals(PDF_SIG);
}
プリサインドURL発行(例:サーバ)
// pseudo: AWS SDK v3
// 1) 応募IDで名前空間分離、2) Content-Typeを固定、3) 有効期限短め
const key = `apply/${applyId}/${crypto.randomUUID()}.pdf`;
const url = await getPresignedUrl({ key, contentType: 'application/pdf', expiresIn: 300 /*sec*/ });
スキャンワークフロー(擬似)
// S3 Putイベント → Queue → Scanner(ClamAV) → タグ付け
if (clamScan(file).infected) {
moveTo('quarantine/', key); tag(key, {safe: 'false', reason: 'virus'});
notify('security@company', `隔離: ${key}`);
} else {
tag(key, {safe: 'true'});
}
8) よくある失敗と回避策
- 拡張子だけチェック → 必ずシグネチャ検査を併用
- 公開バケットに保存 → 非公開+期限付きリンクに統一
- スキャン前に社内展開 → 検査通過まではダウンロード不可の状態に
- 廃棄が手作業 → ライフサイクルルール/定期ジョブで自動削除
9) 運用チェックリスト
- 許可拡張子/サイズ/命名の定義書があり、UIとAPIが同じルール
- アップロードはプリサインドURL、サーバはメタのみ受け取る
- 到着直後に自動スキャン、OK/NGをタグで可視化
- 非公開ストレージ+暗号化+役割ベースのアクセス制御
- 期限付きURLのみで配布(メール添付は原則禁止)
- 保存/廃棄ポリシーが合意・周知・自動化されている
- アクセス/配布ログを保存(第三者提供に該当する場合の記録は3年)
参考・一次情報
- OWASP Cheat Sheet Series:File Upload / Unrestricted File Upload / Malicious Files Testing
- IPA「安全なウェブサイトの作り方」:運用管理20ヶ条 等
- 個人情報保護委員会FAQ:保存期間と消去(法22条)/第三者提供の記録保存(3年)
- AWS:Presigned URL / Best Practices(PDF)
- ClamAV Docs:Scanning / On-Access
このテンプレを導入すれば、CVRを落とさずに安全性と法令配慮を両立できます。実装のサンプル(AWS/Lambda/ClamAV)もご用意できます。必要なら声をかけてください。
コメント