Flutter’da Env Dosyasından Dart Define Metoduna Geçiş
Merhaba değerli okurlar.. Bu yazımızda Flutter uygulamalarımızdaki hassas bilgileri daha güvenilir şekilde yönetmeyi öğreniyor olacağız..
Geçtiğimiz aylarda — epey bir zaman olmuştur- bir mobil uygulamanın bildirim yapısının hassas bilgileri çalınmış ve tüm kullanıcılara bildirimler gönderilmişti. Olayın yaşandığı dönemde çıkan söylentilerden bazıları uygulamanın .env dosyasının patlatıldığı, deşifre edildiği yönündeydi. Bu söylentilerle birlikte geliştirici camiasında .env dosyalarının güvenliği tartışılır oldu.
Env Dosyaları Ne Kadar Güvenli?
Bir çok Flutter geliştiricisininde uygulamaya ait hassas bilgileri .env dosyasında sakladığını biliyoruz. Env dosyaları, hard coded olarak yani kod içinde direkt olarak hassas bilgiyi vermekten bizi kurtarıyor ve kolaylık sağlıyor. Ancak uygulamanın build alınmasıyla birlikte her şey düz bir metin gibi build içerisine eklendiğinden env dosyamız decompile gibi işlemlerde rahatlıkla okunabilir bir seviyeye geliyor. Peki ama bu nasıl oluyor?
Bu işlemi anlayabilmek için önce en başa dönelim. Env dosyası oluşturmak ve uygulama içinde kullanmak için ilgili paketleri ekliyoruz ve pubspec.yaml dosyamızda şu satırları ekliyoruz;
flutter:
assets:
- .env
Bu işlemi yapıp build aldığımızda Flutter ilgili build dosyasının içinde assets adında bir yapı oluşturup içerisine bizim env dosyamızı dahil ediyor. Bu da env dosyamızı ve içerisindeki hassas bilgileri halka açık hale getiriyor.
decompile_edilen_apk/
├── assets/ <-- Oluşturulan dosya
│ └── flutter_assets/
│ ├── .env <-- Ve hassas bilgilerimiz!
│ ├── AssetManifest.json
│ ├── fonts/
│ │ └── MaterialIcons-Regular.otf
│ └── packages/
│ └── ...
├── lib/
├── META-INF/
└── ... (AndroidManifest.xml ve diğer Android sistem dosyaları)
Görüldüğü üzere env dosyasında hassas bilgileri tutmak artık o kadar güvenli bir yöntem değil.
Dart Define İle Güvenliği Sağlamak
Env dosyaları o kadarda güvenli değilse güvenliği nasıl sağlayacağız? Aslında bu sorunun sadece bir yanıtı yok ancak biz elimizdeki imkanları kullanacağız.
Flutter bize env dosyasından çok daha güvenilir bir yaklaşım sunuyor. Uygulamaya ait hassas bilgileri compile time yani derleme anında uygulamaya ekleme imkanı sunuyor. Bu yöntemle birlikte hassas bilgiler ayrı bir asset olarak ele alınmaz, kodun içine gömülür ve deşifre olması daha zorlaşır. Sunulan bu imkana gelin daha yakından bakalım;
Dart Define
— dart-define komutu, Flutter’a tam olarak bunu yapmasını söyler: “Uygulamayı derlerken, sana verdiğim bu gizli bilgiyi al ve doğrudan çalıştırılabilir kodun içine, erişilmesi zor bir yere göm.” ,
Ancak bu metodu kullanabilmek için öncesinde yapmamız gerekenler var.
a) config.json dosyasının oluşturulması
Öncelikle uygulamamızın ana dizininde config.json(dosya adı size kalmış) adında bir dosya oluşturmalıyız. Bu dosyayı oluşturduktan sonra içerisine hassas bilgilerimizi gireceğiz
Örnek bir içeriği paylaşıyorum;
{
"API_KEY": "PRO-SEVIYE-GIZLI-ANAHTAR-98765",
"API_URL": "https://prod-api.guvenliuygulamam.com",
"ANALYTICS_ID": "UA-12345-67"
}
b) config.json dosyasının gitignore edilmesi
Oluşturduğumuz bu dosyayı mutlaka gitignore etmeliyiz. Özellikle github gibi platformlarda projelerimizi paylaştığımız için bu hassas bilgilerin yalnızca bizde olması gerekiyor.
c) config.json dosyasının okunması
Hassas bilgilerimizi ekledik, dosyamızı gitignore ettik. Buraya kadar her şey mükemmel. Sırada bu hassas bilgileri kod içerisinde kullanmak istediğimiz zaman nasıl kullanıyoruz bu var.
// 'API_URL' adıyla derleme sırasında tanımlanan değeri oku.
const String apiUrl = String.fromEnvironment('API_URL');
// Bir değer tanımlanmamışsa ne olacak?
// Hata almamak için bir varsayılan değer (defaultValue) belirleyebiliriz.
// Bu, özellikle opsiyonel özellikler (feature flags) için harikadır.
const bool isPremiumFeatureEnabled = bool.fromEnvironment(
'ENABLE_PREMIUM',
defaultValue: false
);
Bu hassas bilgileri kullanmak için ekstra yapılara gerek yoktur. fromEnvironment metodu ile bu hassas bilgileri kod içerisinde kullanabilirsiniz
d) config.json dosyasıyla build alma
Tüm adımları başarıyla uyguladık ve artık bir apk dosyası almak istiyoruz bunun içinde aşağıdaki kodu terminale yazmanız yeterli olacaktır;
flutter build apk --release --dart-define-from-file=config.json
Bu komutun her bir parçasının anlamı şudur:
- flutter build apk — release: “Flutter, Android için bir sürüm (release) paketi oluştur.”
- — dart-define-from-file=config.json: “…ama bu paketi oluştururken, config.json dosyasının içindeki tüm anahtar/değer çiftlerini al ve derleme zamanı değişkenleri olarak tanımla.”
Güvenlik harika, ama her F5'e — ya da run tuşuna — basmak istediğimde terminale o uzun komutu yazacaksam bu iş yaş dediğinizi duyar gibiyim. Geliştiriciler olarak konforumuz en az güvenlik kadar önemli bir konu, farkındayım =)
Vs Code Üzerinde Gerekli Kısayolun Oluşturulması
Vs Code, Cursor gibi editör kullanan arkadaşların imdadına launch.json yetişiyor. Launch json dosyanızı açıp aşağıdaki gibi bir yapılandırma ekledikten sonra terminal üzerinden uzun uzun ilgili dart define komutunu yazmanıza gerek kalmayacak.
Yapmanız gereken Vs Code ve Vs Code tabanlı editörlerinizde launch.json dosyasını bulmak içerisini aşağıdakine benzer şekilde düzenlemek
{
"version": "0.2.0",
"configurations": [
{
// Bu, projenin tek ve varsayılan başlatma yapılandırmasıdır.
// VS Code'un "Run" panelinde bu isim görünür.
"name": "Flutter",
"request": "launch",
"type": "dart",
// "Run" düğmesine basıldığında, geliştirme sırlarını içeren
// dosyayı otomatik olarak kullanmasını sağlar.
"args": [
"--dart-define-from-file=config.dev.json"
]
}
]
}
Eğer birden fazla ortam varsa yani dev ve release gibi kanallarınız varsa onlara özel olarak yapılandırma yapmak isterseniz aşağıdaki örnek yardımcı olacaktır
{
"version": "0.2.0",
"configurations": [
{
// Bu, "Run and Debug" panelinde göreceğiniz isimdir.
"name": "🚀 Flutter (Geliştirme)",
"request": "launch",
"type": "dart",
// "Run" düğmesine basıldığında `flutter run` komutuna
// eklenecek argümanların listesi.
"args": [
"--dart-define-from-file=config.dev.json"
]
},
// BONUS: Farklı ortamlar için farklı başlatıcılar da ekleyebilirsiniz.
{
"name": "🔥 Flutter (Canlı Test)",
"request": "launch",
"type": "dart",
"flutterMode": "release", // Bu, release modunda başlatır
"args": [
"--dart-define-from-file=config.json" // Canlı (prod) sırlarını kullanır
]
}
]
}
Bu Vs Code için peki Android Studio kullanan alfalar ne yapacak dediğinizide işittim =)
Android St. Üzerinde Gerekli Kısayolun Oluşturulması
Bu adımları görsellerle anlatmaya çalışacağım.
- Öncelikle projenizi açın. Projenizi açıktan sonra emülatör adının göründüğü kısmın hemen sağında yer alan Add Configuration’a basın. Bastığınızda Edit Configuration seçeneği çıkacak. Ona basıp sonraki adımlara geçin
- Karşınıza gelen ekranda yazılı Add New Run Configuration yazısına basıyorsunuz sizi ilgili ekrana yönlendiriyor
- Açılan ilgili ekrandan Flutter kısmını seçiyorsunuz ve configuration name tanımlıyorsunuz. Daha sonra sizden projenin main dart dosyasını seçmenizi ve run args istiyor. Yani run butonuna basınca çalışacak metodu istiyor onuda yazdıktan sonra apply diyip kaydediyorsunuz.Tüm işlemler bu kadar.
Flutter uygulamanızda varolan env dosyalarınızı bu şekilde çok daha güvenli bir yapıya taşıyabilir, yeni başladığınız projelerde bu metodu benimseyip hassas bilgiler için daha güvenli bir katman oluşturabilirsiniz.
Bu yazımda sizlere env yapısından çok daha güvenli bir yapı olduğunu inandığım dart define metodunu anlatmaya çalıştım. Umarım faydalı olur ilgilisi için.
Okuyan ve destekleyen tüm okurlara teşekkür eder, çalışmalarınızda kolaylıklar dilerim!
Github: www.github.com/abdullah017
Linkedin: www.linkedin.com/in/abdullahtas
Stackoverflow: https://stackoverflow.com/users/13807726/abdullah-t