[firebase-ml-modeldownloader] downloadUri from server response is not validated against expected Google/Firebase domains before being passed to DownloadManager
Maintainer thường phản hồi trong vòng 1 ngày
@rlazo đang làm issue này rồi.
Từ ngày 13/4/2026.
Đánh giá
Issue này chưa được đánh giá.
Mô tả
Summary
The firebase-ml-modeldownloader SDK fetches model metadata from firebaseml.googleapis.com and receives a downloadUri field in the JSON response. This URI is passed directly and without any validation to DownloadManager.enqueue(), which then downloads the file and stores it in the app's private storage as a trusted ML model.
The SDK is designed to exclusively download models hosted on Firebase/Google Cloud Storage — there is no public API for developers to supply a custom download URL. Despite this, the code places zero constraints on the downloadUri value at the point of consumption, creating an unnecessary and avoidable risk surface.
Affected Files
| File | Line | Action |
|---|---|---|
firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/CustomModelDownloadService.java |
~370 | downloadUrl = reader.nextString() — raw URL read from JSON, no validation |
firebase-ml-modeldownloader/src/main/java/com/google/firebase/ml/modeldownloader/internal/ModelFileDownloadService.java |
~283 | downloadManager.enqueue(new Request(Uri.parse(customModel.getDownloadUrl()))) — unvalidated URL passed to DownloadManager |
Code Flow
Firebase ML backend response (JSON):
{ "downloadUri": "<url>", "expireTime": "...", "sizeBytes": 1234 }
→ CustomModelDownloadService.readCustomModelResponse() [line ~370]
→ downloadUrl = reader.nextString() ← NO host/scheme check
→ CustomModel object stores the raw URL
→ ModelFileDownloadService.scheduleModelDownload() [line ~283]
→ new Request(Uri.parse(customModel.getDownloadUrl()))
→ downloadManager.enqueue(request)
→ Android DownloadManager fetches the URL
→ File written to app private storage as a trusted ML model
Why This Is a Problem
The downloadUri in the server response is always expected to be a Google Cloud Storage URL (e.g., https://firebasestorage.googleapis.com/... or https://storage.googleapis.com/...). The SDK was never designed to accept model files from third-party hosts.
However, the code does not enforce this expectation. Any URL that reaches DownloadManager.enqueue() will be fetched and stored unconditionally.
Scenarios where a non-Google URL could reach this code path:
- Corporate/enterprise network with MDM-managed CA: extremely common in enterprise Android deployments where a trusted proxy CA is pushed via MDM, enabling transparent TLS inspection of all HTTPS traffic
- Compromised or misconfigured Firebase ML backend (supply-chain scenario)
- Future regression — a developer refactoring the URL construction logic could inadvertently introduce an externally-influenced URL without realising there is no downstream validation
Note: This is not claimed as a trivially exploitable remote attack. It is a defense-in-depth gap: the SDK makes a strong implicit assumption about the URL it receives but never validates it, when doing so would be trivial and non-breaking.
Impact
If a non-Google URL were to reach DownloadManager.enqueue() through any of the above paths:
- Arbitrary file download — any binary can be written to the app's private
NoBackupFilesDirundercom.google.firebase.ml.custom.models/, labeled as a trusted model - ML integrity violation — even without code execution, a substituted model (e.g., a fraud-detection model that always outputs "not fraud") silently corrupts the app's ML-dependent behaviour
- Potential memory corruption — the downloaded file is subsequently loaded by TensorFlow Lite. TFLite has a documented history of memory corruption vulnerabilities when parsing malformed model files (e.g., CVE-2022-23553), making this a plausible escalation path on devices with vulnerable TFLite versions
Proposed Fix
1. Validate downloadUri against an allowlist of expected Firebase/Google domains
// In CustomModelDownloadService.java — where downloadUrl is read from JSON
} else if (name.equals("downloadUri")) {
String rawUrl = reader.nextString();
Uri parsed = Uri.parse(rawUrl);
String host = parsed.getHost();
if (!"https".equals(parsed.getScheme()) ||
host == null ||
(!host.endsWith(".googleapis.com") &&
!host.endsWith(".firebasestorage.app") &&
!host.endsWith(".storage.googleapis.com"))) {
throw new IOException("downloadUri host is not an allowlisted Firebase/Google domain: " + host);
}
downloadUrl = rawUrl;
}
This fix has zero impact on legitimate usage — all real Firebase ML download URLs already come from .googleapis.com or .firebasestorage.app. No developer-facing API change is required.
2. (Recommended) Verify model file integrity before loading
The server response already includes a modelHash / ETag value. Verifying the SHA-256 of the downloaded file against this hash before passing it to TFLite would eliminate the file-substitution risk entirely, even in the absence of URL validation.
References
- Ngôn ngữ chính
- Java
- Star
- 2.6k
- Fork
- 733
- Merge trung bình
- 2 ngày 15 giờ
- Pull request đã merge (30 ngày)
- 72
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Không có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của firebase/firebase-android-sdk
-
api: remoteconfig
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
firebase/firebase-android-sdk#8827 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Crashlytics plugin writes build ID lists in directory listing order, so APKs can differ between machinesCó thể đã có người làm @mrober đã nhận 8 ngày trước. Đang mởapi: crashlytics
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
firebase/firebase-android-sdk#8789 · 1 bình luận · 1 người được giao ·
Maintainer thường phản hồi trong vòng 1 ngày
-
api: auth
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
firebase/firebase-android-sdk#8505 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 15/100
firebase/firebase-android-sdk#8835 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 25/100
firebase/firebase-android-sdk#8832 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của firebase/firebase-android-sdk
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
NationalSecurityAgency/ghidra#10220 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 80/100
Maintainer thường phản hồi trong vòng 1 ngày
-
RIS export splits an organization name containing “and” into multiple authorsCó thể đã có người làm @Gautam-aman đã nhận hôm nay. Đang mởstatus: needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
JabRef/jabref#17821 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
FailureContext.java rounds down thresholds in lastedAtLeast()Có thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày