This episode covers security and data handling in Dart: safe input validation, encryption and secure storage, secrets management, network calls with HTTPS and certificates, and secure coding practices in Dart.

An application that isn't secure doesn't deserve to be called finished. Episode 13 covers security and data handling in Dart from a practical angle: validating incoming input, encrypting sensitive data, storing secrets properly, calling the network only over HTTPS, and building secure coding habits.
Security isn't a feature you add at the end — it's a decision made at every layer: from input parsing, to storage, to network communication. This time, you'll put it into practice directly.
User input is the primary attack vector. Never trust input without validation:
bool emailValid(String email) {
final pola = RegExp(r'^[^@]+@[^@]+\.[^@]+$');
return pola.hasMatch(email);
}
void main() {
print(emailValid('arman@example.com'));
print(emailValid('bukan-email'));
}RegExp(r'^[^@]+@[^@]+\.[^@]+$') checks the email format before the data is processed any further. Validation prevents injection and input-based attacks at every layer of the application.
When interacting with a database, never build queries by concatenating strings directly. Always use the parameter binding provided by the database driver. The same principle applies to shell commands and HTML — separate data from the code that gets executed.
Sensitive data such as tokens and keys must be encrypted while stored or in transit:
import 'package:cryptography/cryptography.dart';
Future<void> main() async {
final algoritma = AesGcm.with256bits();
final kunci = await algoritma.newSecretKey();
final cipher = await algoritma.encrypt(
const utf8.encode('data rahasia'),
secretKey: kunci,
);
print(cipher.cipherText.length);
}AesGcm.with256bits() is a secure, authenticated AES-GCM encryption mode. Store the key in the right place — see the next section — and never put keys in code.
For Flutter, don't store sensitive data in SharedPreferences. Use flutter_secure_storage, which leverages the Android keystore and iOS Keychain:
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
final storage = const FlutterSecureStorage();
Future<void> simpanToken(String token) async {
await storage.write(key: 'access_token', value: token);
}storage.write(key: 'access_token', value: token) stores the token in the platform's secure storage. For the backend, keep sensitive data in protected environment variables, not in configuration files that get committed.
API keys, database passwords, and tokens don't belong in source control. Use environment variables:
export DB_PASSWORD='hanya-di-environment'
dart run bin/server.dartexport DB_PASSWORD='...' supplies the secret value through the process environment. In CI/CD, fill secrets via the platform's secret store (for example GitHub Secrets) and inject them at build time.
For local development, read a .env file that isn't committed:
dart run --define=from-file=.env bin/app.dartdart run --define=from-file=.env loads definitions from .env without printing them to logs. Make sure .env is in .gitignore so secrets are never committed.
All network communication must go over HTTPS with certificate verification enabled. Don't disable verification:
import 'package:http/http.dart' as http;
Future<void> main() async {
var resp = await http.get(Uri.parse('https://api.example.com/data'));
print(resp.statusCode);
}http.get(Uri.parse('https://api.example.com/data')) uses default TLS with full certificate verification. Disabling verification via badCertificateCallback opens a man-in-the-middle hole — only do it in deliberate local testing.
For servers, provide certificates from a trusted CA, not self-signed ones in production. Tools like Let's Encrypt provide free, automatic certificates. Never hardcode certificate pinning that's hard to maintain without a clear security need.
Some habits that keep applications safe:
dart pub outdated catches packages with CVEs.analysis_options.yaml.Enable the relevant lints by adding the lints or pedantic package to dev_dependencies, then run dart analyze regularly to detect risky patterns.
Key takeaways:
flutter_secure_storage for tokens in Flutter.dart pub outdated and dart analyze regularly.In the next episode 14, we'll cover tooling and build configuration — build_runner and code generation, static analysis with dart analyze, formatting with dart format, and building a CI/CD pipeline for Dart projects.