Hardcoded credential / secret in Dart source
ID |
dart.auth.hardcoded_credentials |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
high |
Remediation Effort |
medium |
Resource |
Auth |
Language |
Dart |
Description
A credential / secret is embedded as a string literal in source: a top-level / class-field const / final / String declaration named api_key, access_token, refresh_token, client_secret, password, bearer, etc. assigned a non-trivial literal. The Dart obfuscator does not protect string literals — anyone with the binary has this credential.
Rationale
Cossack Labs documented that flutter build --obfuscate
obfuscates symbols but NOT string literals: the literal bytes
survive intact in the shipped Dart snapshot and are recoverable
with analyze_snapshot, blutter, or a hex editor. Hardcoded
credentials in Flutter source must therefore be considered
public — equivalent to publishing them on GitHub.
Impact varies by credential type:
-
API keys (Firebase, Stripe, Sentry DSN, Google Maps, AWS): let the attacker call the upstream API with your billing account; abuse can run into thousands of dollars before detection, and the API key may grant access to user data.
-
OAuth client secrets: enable token impersonation against your backend.
-
JWT signing secrets: total authentication forgery.
-
Bearer / Basic auth literals in headers: replay against the target service.
The rule also matches an Authorization: Bearer <long-string>
header literal — a common shape in copy-pasted curl examples
that survive into committed code.
The following code illustrates a vulnerable pattern detected by this rule:
// Positive cases for dart.hardcoded_credentials.
class Config {
// VULNERABLE: Hardcoded credential / secret in Dart source
static const String apiKey = "sk_live_abcdef1234567890";
}
Remediation
Load secrets at runtime from flutter_secure_storage, fetch
them from an authenticated backend after sign-in, or — for
build-time configuration — inject via --dart-define /
--dart-define-from-file and read with String.fromEnvironment.
For values that genuinely need to ship with the app (a public
API key with documented usage limits), confirm with the upstream
service that public exposure is acceptable.
// Vulnerable
static const String apiKey = "sk_live_abcdef1234567890";
static const String clientSecret = "OAuth-Secret-1234567890";
final headers = {"Authorization": "Bearer eyJhbGc..."};
// Fixed: build-time injection
static const String apiKey = String.fromEnvironment('STRIPE_API_KEY');
// build with: flutter build apk --dart-define=STRIPE_API_KEY=sk_live_...
// Fixed: runtime from secure storage
const storage = FlutterSecureStorage();
final token = await storage.read(key: 'access_token');
References
-
OWASP Top 10 2025 - A07 : Authentication Failures.
-
OWASP Top 10 2025 - A02 : Security Misconfiguration.
-
Cossack Labs — Flutter application security considerations — Documents the Dart obfuscator’s no-op on string literals
-
Talsec — OWASP Mobile Top 10 for Flutter M1 — Mastering credential security in Flutter (M1 — Improper Credential Usage)
-
package:flutter_secure_storage (pub.dev) — Keychain / Keystore-backed storage — the recommended runtime location for secrets