説明
ユーザー入力をファイルパスとして直接使用すると、攻撃者が ../ などのパス要素を使い、許可されたディレクトリの外にあるファイルを読み取ったり変更したりするおそれがあります。
想定される影響
- 機密ファイルの読み取り。
- 任意のファイルの削除や上書き。
- アプリのサンドボックス内にあるデータの漏えい。
対処方法
- ユーザー入力を直接ファイルパスとして使用しないでください。
- 基準ディレクトリと結合して正規化し、結果がそのディレクトリ内に収まることを確認してください。
- ファイル名の文字と拡張子を許可リストで制限してください。
例
変更前
swift
let fileName = readLine()!
let contents = try String(contentsOfFile: fileName)
変更後
swift
import Foundation
enum PathValidationError: Error {
case invalidFileName
case outsideBaseDirectory
}
func readUpload(fileName: String, baseDirectory: URL) throws -> String {
guard !fileName.isEmpty,
fileName == URL(fileURLWithPath: fileName).lastPathComponent else {
throw PathValidationError.invalidFileName
}
// baseDirectory는 앱만 쓸 수 있는 디렉터리여야 합니다.
let base = baseDirectory
.resolvingSymlinksInPath()
.standardizedFileURL
let candidate = base
.appendingPathComponent(fileName, isDirectory: false)
.resolvingSymlinksInPath()
.standardizedFileURL
guard candidate.deletingLastPathComponent() == base else {
throw PathValidationError.outsideBaseDirectory
}
return try String(contentsOf: candidate, encoding: .utf8)
}
解説:
- 変更前: ユーザー入力がファイルパス全体を決めます。
- 変更後: 単一のファイル名だけを許可し、基準ディレクトリと候補パスのシンボリックリンクを解決して、実際の親ディレクトリが基準ディレクトリと一致する場合だけ読み取ります。例の前提どおり、基準ディレクトリを変更できるのはアプリだけにしてください。検証後に攻撃者がパスを置き換えられないよう保護する必要があります。