Proč je zabezpečení mobilních aplikací důležitější než kdy dřív
Mobilní aplikace dnes uchovávají a přenášejí obrovské množství citlivých dat — přihlašovací údaje, tokeny, osobní informace, platební data. A přitom jsou v porovnání se serverovými aplikacemi mnohem zranitelnější. Zařízení se ztrácejí, lidi se připojují k nezabezpečeným Wi-Fi sítím a instalují si kde co, včetně aplikací, které můžou být škodlivé.
Podle OWASP Mobile Top 10 patří mezi nejčastější bezpečnostní chyby mobilních aplikací nezabezpečené ukládání dat, slabá autentizace a nedostatečná ochrana síťové komunikace. A .NET MAUI aplikace rozhodně nejsou výjimkou — pokud bezpečnost aktivně neřešíte, vaše aplikace má skoro jistě nějaké slabiny.
V tomhle článku si projdeme kompletní bezpečnostní strategii pro .NET MAUI aplikace. Od správného ukládání citlivých dat přes biometrickou autentizaci až po certificate pinning a ochranu API klíčů. Všechno s praktickými příklady kódu, které můžete rovnou vzít a implementovat. Tak pojďme na to.
Secure Storage: Jak správně uchovávat citlivá data
.NET MAUI poskytuje vestavěný mechanismus pro bezpečné ukládání dat prostřednictvím rozhraní ISecureStorage. Na pozadí využívá nativní šifrovací mechanismy každé platformy — na Androidu EncryptedSharedPreferences s AES-256 GCM šifrováním, na iOS Keychain a na Windows DataProtectionProvider. Což je fajn, protože se nemusíte starat o nízkoúrovňovou kryptografii sami.
Základní použití Secure Storage
Uložení a načtení tokenu je v zásadě jednoduché, ale je potřeba ošetřit výjimky — Secure Storage může selhat třeba na zařízeních s nestandardní konfigurací (a věřte mi, takových je víc, než byste čekali):
public class TokenService : ITokenService
{
private const string AccessTokenKey = "access_token";
private const string RefreshTokenKey = "refresh_token";
public async Task SaveTokensAsync(string accessToken, string refreshToken)
{
try
{
await SecureStorage.Default.SetAsync(AccessTokenKey, accessToken);
await SecureStorage.Default.SetAsync(RefreshTokenKey, refreshToken);
}
catch (Exception ex)
{
// Na některých zařízeních může SecureStorage selhat
// (např. starší Android bez hardware-backed keystore)
Debug.WriteLine($"SecureStorage error: {ex.Message}");
throw new SecurityException(
"Nepodařilo se bezpečně uložit přihlašovací údaje.", ex);
}
}
public async Task<string?> GetAccessTokenAsync()
{
try
{
return await SecureStorage.Default.GetAsync(AccessTokenKey);
}
catch (Exception ex)
{
Debug.WriteLine($"SecureStorage read error: {ex.Message}");
// Token nelze načíst — vynutíme nové přihlášení
return null;
}
}
public void ClearTokens()
{
SecureStorage.Default.Remove(AccessTokenKey);
SecureStorage.Default.Remove(RefreshTokenKey);
}
}
Registrace služby v DI kontejneru
Službu pro správu tokenů zaregistrujte jako singleton v MauiProgram.cs:
builder.Services.AddSingleton<ITokenService, TokenService>();
Pozor na Android Auto Backup
Tohle je past, na kterou narazí spousta vývojářů. Sám jsem na to jednou naběhl a strávil pěkných pár hodin debugováním.
Android 6.0+ automaticky zálohuje data aplikace, včetně SharedPreferences, do Google cloudu. Problém? Když se data obnoví na novém zařízení, dešifrovací klíč je jiný a SecureStorage přestane fungovat. Uživatel se prostě nemůže přihlásit a vy nevíte proč.
Řešení je vyloučit SecureStorage data ze zálohy. Vytvořte soubor Platforms/Android/Resources/xml/backup_rules.xml:
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude domain="sharedpref"
path="__maui_secure_storage" />
</cloud-backup>
<device-transfer>
<exclude domain="sharedpref"
path="__maui_secure_storage" />
</device-transfer>
</data-extraction-rules>
A v AndroidManifest.xml přidejte odkaz na pravidla:
<application
android:dataExtractionRules="@xml/backup_rules"
...>
</application>
Biometrická autentizace: Otisk prstu a Face ID
Biometrická autentizace je dnes standard u bankovních a finančních aplikací, ale upřímně — hodí se prakticky všude, kde chcete uživateli nabídnout rychlý a bezpečný přístup. Nikdo nechce pokaždé vyťukávat heslo. V .NET MAUI máte k dispozici několik pluginů, ale nejrobustnější je Plugin.Maui.Biometric, který podporuje Android, iOS, macOS i Windows.
Instalace a konfigurace
Nainstalujte NuGet balíček:
dotnet add package Plugin.Maui.Biometric
Zaregistrujte plugin v MauiProgram.cs:
using Plugin.Maui.Biometric;
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseBiometricAuthentication();
return builder.Build();
}
Konfigurace pro Android a iOS
Na Androidu přidejte oprávnění do AndroidManifest.xml:
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
Na iOS přidejte popis použití Face ID do Info.plist. Bez něj vám aplikace na iOS 11.3+ spadne při pokusu o Face ID autentizaci — a to je docela nepříjemné překvapení při review v App Store:
<key>NSFaceIDUsageDescription</key>
<string>Aplikace používá Face ID pro bezpečné přihlášení.</string>
Implementace biometrického přihlášení
Vytvořte službu, která zapouzdří biometrickou autentizaci a elegantně řeší situace, kdy biometrie není dostupná:
public class BiometricService : IBiometricService
{
private readonly IBiometricAuthentication _biometric;
public BiometricService(IBiometricAuthentication biometric)
{
_biometric = biometric;
}
public async Task<bool> IsBiometricAvailableAsync()
{
var result = await _biometric.GetEnrollmentStatusAsync();
return result == BiometricEnrollmentStatus.Enrolled;
}
public async Task<BiometricAuthResult> AuthenticateAsync()
{
// Zkontrolujeme dostupnost
var isAvailable = await IsBiometricAvailableAsync();
if (!isAvailable)
{
return new BiometricAuthResult
{
Success = false,
ErrorMessage = "Biometrie není na tomto zařízení dostupná."
};
}
var request = new AuthenticationRequest
{
Title = "Ověření identity",
Subtitle = "Přihlaste se pomocí otisku prstu nebo Face ID",
NegativeText = "Zrušit"
};
var result = await _biometric.AuthenticateAsync(
request, CancellationToken.None);
return new BiometricAuthResult
{
Success = result.Status == BiometricResponseStatus.Success,
ErrorMessage = result.Status != BiometricResponseStatus.Success
? result.ErrorMsg
: null
};
}
}
public record BiometricAuthResult
{
public bool Success { get; init; }
public string? ErrorMessage { get; init; }
}
Použití ve ViewModelu
V praxi typicky kombinujete biometrii se Secure Storage. Uživatel se poprvé přihlásí klasicky (jméno + heslo), token uložíte do Secure Storage a při dalším spuštění aplikace mu nabídnete biometrické ověření. Je to přesně ten flow, na který jsou lidi zvyklí z bankovních appek:
public partial class LoginViewModel : ObservableObject
{
private readonly IBiometricService _biometric;
private readonly ITokenService _tokenService;
private readonly IAuthApiService _authApi;
public LoginViewModel(
IBiometricService biometric,
ITokenService tokenService,
IAuthApiService authApi)
{
_biometric = biometric;
_tokenService = tokenService;
_authApi = authApi;
}
[RelayCommand]
private async Task TryBiometricLoginAsync()
{
// Máme uložený token?
var storedToken = await _tokenService.GetAccessTokenAsync();
if (storedToken is null)
{
// Žádný token — uživatel se musí přihlásit klasicky
return;
}
// Ověříme biometrií
var authResult = await _biometric.AuthenticateAsync();
if (!authResult.Success)
{
await Shell.Current.DisplayAlert(
"Chyba", authResult.ErrorMessage, "OK");
return;
}
// Biometrie úspěšná — ověříme platnost tokenu
var isValid = await _authApi.ValidateTokenAsync(storedToken);
if (isValid)
{
await Shell.Current.GoToAsync("//main");
}
else
{
// Token expiroval — vyčistíme a vynutíme přihlášení
_tokenService.ClearTokens();
}
}
}
Certificate pinning: Ochrana proti man-in-the-middle útokům
HTTPS samo o sobě nezaručuje, že komunikujete s tím správným serverem. Útočník s přístupem k síti (třeba přes veřejnou Wi-Fi v kavárně) může za určitých okolností provést man-in-the-middle útok podvržením certifikátu. Certificate pinning tomuhle zabraňuje tím, že aplikace akceptuje pouze konkrétní certifikát nebo veřejný klíč serveru.
Zní to jednoduše, ale implementace má pár háčků.
Implementace pomocí HttpClientHandler
Nejčistší způsob v .NET MAUI je vlastní HttpClientHandler s validačním callbackem. Pinujeme na veřejný klíč (SPKI hash), ne na celý certifikát — díky tomu si nezkomplikujete život při rotaci certifikátů:
public class CertificatePinningHandler : HttpClientHandler
{
// SHA-256 hash veřejného klíče vašeho serveru
// Získáte příkazem:
// openssl x509 -in cert.cer -pubkey -noout |
// openssl pkey -pubin -outform der |
// openssl dgst -sha256 -binary |
// openssl enc -base64
private static readonly HashSet<string> PinnedKeys = new()
{
"YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=",
// Záložní pin pro případ rotace certifikátu
"sRHdihwgkaib1P1gN7SkKPuHQc7e3HJhNv8Cl8JKISM="
};
public CertificatePinningHandler()
{
ServerCertificateCustomValidationCallback = ValidateServerCertificate;
}
private static bool ValidateServerCertificate(
HttpRequestMessage message,
X509Certificate2? certificate,
X509Chain? chain,
SslPolicyErrors sslErrors)
{
if (certificate is null)
return false;
// Spočítáme SHA-256 hash veřejného klíče
var publicKeyBytes = certificate.GetPublicKey();
using var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(publicKeyBytes);
var pinHash = Convert.ToBase64String(hash);
return PinnedKeys.Contains(pinHash);
}
}
Registrace v DI kontejneru
Pro produkční build použijeme pinning handler, pro vývoj standardní handler. Jinak byste si dost zkomplikovali práci s lokálním HTTPS (a to fakt nechcete řešit při každém F5):
// MauiProgram.cs
builder.Services.AddTransient<CertificatePinningHandler>();
builder.Services.AddHttpClient("SecureApi", client =>
{
client.BaseAddress = new Uri("https://api.mojeaplikace.cz");
})
#if !DEBUG
.ConfigurePrimaryHttpMessageHandler<CertificatePinningHandler>();
#else
;
#endif
Alternativa: SecureHttpClient NuGet balíček
Pokud preferujete jednodušší API a nechcete si psát vlastní handler, můžete sáhnout po balíčku SecureHttpClient:
var handler = new SecureHttpClientHandler();
handler.AddCertificatePinner("api.mojeaplikace.cz", new string[]
{
"sha256/YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg="
});
var client = new HttpClient(handler);
var response = await client.GetAsync("https://api.mojeaplikace.cz/data");
Certificate pinning na Androidu pomocí Network Security Config
Android nabízí i nativní podporu přes network_security_config.xml. Tohle řešení funguje na úrovni platformy a pokrývá veškerou síťovou komunikaci, nejen HTTP klienta — což je docela podstatný rozdíl:
<!-- Platforms/Android/Resources/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="false">
<domain includeSubdomains="true">api.mojeaplikace.cz</domain>
<pin-set expiration="2027-01-01">
<pin digest="SHA-256">
YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=
</pin>
<pin digest="SHA-256">
sRHdihwgkaib1P1gN7SkKPuHQc7e3HJhNv8Cl8JKISM=
</pin>
</pin-set>
</domain-config>
</network-security-config>
Odkažte na konfiguraci v AndroidManifest.xml:
<application
android:networkSecurityConfig="@xml/network_security_config"
...>
</application>
Ochrana API klíčů: Nikdy je nezabalujte do aplikace
Tohle je jedno z nejčastějších bezpečnostních pochybení v mobilních aplikacích. A vidím to pořád dokola. API klíče hardcoded ve zdrojovém kódu nebo konfiguračních souborech jsou snadno extrahovatelné — stačí dekompilovat APK nebo IPA soubor. Doslova pár minut práce.
Správný přístup k API klíčům
Místo toho, aby vaše aplikace přímo volala třetí stranu s API klíčem, vložte mezi ně vlastní backend jako proxy:
// ŠPATNĚ — API klíč přímo v aplikaci
public class WeatherService
{
private const string ApiKey = "sk-abc123..."; // Snadno extrahovatelné!
public async Task<Weather> GetWeatherAsync(string city)
{
var url = $"https://api.weather.com/v1?key={ApiKey}&city={city}";
// ...
}
}
// SPRÁVNĚ — volání přes vlastní backend
public class WeatherService
{
private readonly HttpClient _httpClient;
private readonly ITokenService _tokenService;
public WeatherService(
IHttpClientFactory factory,
ITokenService tokenService)
{
_httpClient = factory.CreateClient("MyBackend");
_tokenService = tokenService;
}
public async Task<Weather> GetWeatherAsync(string city)
{
// Backend ověří uživatelský token a přidá API klíč
var token = await _tokenService.GetAccessTokenAsync();
_httpClient.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", token);
var response = await _httpClient.GetAsync(
$"/api/weather?city={city}");
response.EnsureSuccessStatusCode();
return await response.Content
.ReadFromJsonAsync<Weather>();
}
}
Bezpečnostní konfigurace pro build
Pokud přesto musíte uchovávat nějaké konfigurační hodnoty v aplikaci (třeba URL backendu — to je celkem běžný případ), nikdy je nedávejte přímo do zdrojového kódu. Používejte build proměnné a podmíněnou kompilaci:
// V .csproj souboru
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<DefineConstants>$(DefineConstants);PRODUCTION</DefineConstants>
</PropertyGroup>
// V kódu
public static class AppConfig
{
public static string ApiBaseUrl =>
#if PRODUCTION
"https://api.mojeaplikace.cz";
#else
"https://staging-api.mojeaplikace.cz";
#endif
}
Zabezpečení síťové komunikace
Kromě certificate pinningu existuje řada dalších opatření pro zabezpečení síťové vrstvy vaší aplikace. No a teď se na ně podíváme.
Vynucení HTTPS
Na Androidu zakažte plaintext (HTTP) komunikaci v network_security_config.xml:
<network-security-config>
<base-config cleartextTrafficPermitted="false" />
</network-security-config>
Na iOS přidejte App Transport Security nastavení do Info.plist. iOS ve výchozím stavu vyžaduje HTTPS, ale radši se ujistěte, že jste tam omylem nepřidali výjimky (viděl jsem projekty, kde někdo při vývoji přidal NSAllowsArbitraryLoads a zapomněl to pak odebrat):
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<false/>
</dict>
Bezpečný HttpClient s automatickým obnovením tokenů
V produkčních aplikacích potřebujete DelegatingHandler, který automaticky obnovuje expirované access tokeny pomocí refresh tokenu. Bez toho by se uživatel musel opakovaně přihlašovat, což je otravné a vede to k tomu, že si lidi volí jednodušší hesla:
public class AuthenticatedHttpHandler : DelegatingHandler
{
private readonly ITokenService _tokenService;
private readonly IAuthApiService _authApi;
private readonly SemaphoreSlim _semaphore = new(1, 1);
public AuthenticatedHttpHandler(
ITokenService tokenService,
IAuthApiService authApi)
{
_tokenService = tokenService;
_authApi = authApi;
InnerHandler = new HttpClientHandler();
}
protected override async Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
var token = await _tokenService.GetAccessTokenAsync();
if (token is not null)
{
request.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", token);
}
var response = await base.SendAsync(request, cancellationToken);
// Pokud server vrátí 401, zkusíme obnovit token
if (response.StatusCode == HttpStatusCode.Unauthorized)
{
await _semaphore.WaitAsync(cancellationToken);
try
{
var newToken = await _authApi.RefreshTokenAsync();
if (newToken is not null)
{
await _tokenService.SaveTokensAsync(
newToken.AccessToken,
newToken.RefreshToken);
// Zopakujeme původní požadavek s novým tokenem
request.Headers.Authorization =
new AuthenticationHeaderValue(
"Bearer", newToken.AccessToken);
response = await base.SendAsync(
request, cancellationToken);
}
}
finally
{
_semaphore.Release();
}
}
return response;
}
}
Bezpečné kódování: Checklist podle OWASP Mobile Top 10
OWASP (Open Worldwide Application Security Project) pravidelně publikuje seznam deseti nejkritičtějších bezpečnostních rizik mobilních aplikací. Pojďme si projít ta nejdůležitější v kontextu .NET MAUI a jak se jim bránit. Nebude to vyčerpávající výčet, ale pokryje to to podstatné.
M1: Nedostatečná ochrana přihlašovacích údajů
- Ukládejte tokeny výhradně v
SecureStorage, nikdy vPreferences - Implementujte automatický logout po definované době nečinnosti
- Při odhlášení vymažte všechna uložená data ze
SecureStorage
M2: Nedostatečná kryptografie
- Neimplementujte vlastní šifrovací algoritmy — prosím, fakt ne. Používejte osvědčené knihovny
- Pro lokální šifrování používejte
Aes.Create()s klíčem uloženým vSecureStorage - Nikdy neukládejte šifrovací klíče vedle šifrovaných dat
M4: Nedostatečná validace vstupů
- Validujte vstupy jak na klientovi, tak na serveru
- Sanitizujte textové vstupy před zobrazením v
WebView(prevence XSS) - Používejte parametrizované dotazy při práci s SQLite
// ŠPATNĚ — SQL injection
var query = $"SELECT * FROM Users WHERE Name = '{userInput}'";
// SPRÁVNĚ — parametrizovaný dotaz
var query = "SELECT * FROM Users WHERE Name = @name";
using var command = new SqliteCommand(query, connection);
command.Parameters.AddWithValue("@name", userInput);
M5: Nezabezpečená komunikace
- Vždy používejte HTTPS
- Implementujte certificate pinning pro kritická API
- Zakažte plaintext komunikaci na obou platformách
- Neposílejte citlivá data v URL parametrech — použijte POST body nebo HTTP hlavičky
M8: Manipulace s kódem a reverse engineering
- Aktivujte trimming a NativeAOT v release buildech — tím se výrazně ztíží dekompilace
- Implementujte detekci root/jailbreak zařízení
- Zvažte použití obfuskačních nástrojů jako Dotfuscator
<!-- V .csproj pro release build -->
<PropertyGroup Condition="'$(Configuration)' == 'Release'">
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>full</TrimMode>
<EnableLLVM>true</EnableLLVM>
<RunAOTCompilation>true</RunAOTCompilation>
<!-- AndroidEnableProfiledAot pro Android -->
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>
Detekce root a jailbreak zařízení
Rootnutá a jailbreaknutá zařízení představují bezpečnostní riziko, protože obchází systémové ochrany. U aplikací pracujících s citlivými daty (bankovnictví, zdravotnické aplikace) je vhodné taková zařízení detekovat a uživatele na to upozornit. Tady je základní implementace — není to neprůstřelné, ale pokryje většinu běžných případů:
public static class DeviceSecurityCheck
{
#if ANDROID
public static bool IsDeviceRooted()
{
// Kontrola běžných indikátorů rootu
var rootIndicators = new[]
{
"/system/app/Superuser.apk",
"/system/xbin/su",
"/system/bin/su",
"/sbin/su",
"/data/local/xbin/su",
"/data/local/bin/su"
};
return rootIndicators.Any(path =>
Java.IO.File.Exists(path));
}
#elif IOS
public static bool IsDeviceRooted()
{
// Kontrola běžných indikátorů jailbreaku
var jailbreakPaths = new[]
{
"/Applications/Cydia.app",
"/Library/MobileSubstrate/MobileSubstrate.dylib",
"/bin/bash",
"/usr/sbin/sshd",
"/etc/apt",
"/private/var/lib/apt/"
};
return jailbreakPaths.Any(path =>
System.IO.File.Exists(path));
}
#else
public static bool IsDeviceRooted() => false;
#endif
}
Bezpečné logování a crash reporting
Logování je nezbytné pro diagnostiku, ale může se stát překvapivě velkým bezpečnostním rizikem, pokud do logů prosakují citlivé údaje. A ono se to stane snáz, než si myslíte — stačí jeden neopatrný Debug.WriteLine a máte v logách celý bearer token.
public static class SecureLogger
{
public static void LogApiCall(string endpoint, string? token)
{
// ŠPATNĚ:
// Debug.WriteLine($"API call to {endpoint} with token {token}");
// SPRÁVNĚ — nikdy nelogujte tokeny ani přihlašovací údaje
Debug.WriteLine($"API call to {endpoint}");
}
public static void LogUserAction(string action, string userId)
{
// Logujte pouze anonymizované nebo neidentifikovatelné údaje
var hashedId = Convert.ToBase64String(
SHA256.HashData(Encoding.UTF8.GetBytes(userId)))[..8];
Debug.WriteLine($"User {hashedId} performed: {action}");
}
}
V release buildu zajistěte, aby se debug logy vůbec nezapisovaly. Využijte podmíněnou kompilaci:
[Conditional("DEBUG")]
public static void DebugLog(string message)
{
Debug.WriteLine(message);
}
Kompletní bezpečnostní architektura
No a teď si pojďme shrnout všechny vrstvy zabezpečení do ucelené architektury, kterou můžete postupně implementovat ve svém projektu. Nemusíte všechno nasadit najednou — začněte tím nejdůležitějším (Secure Storage, HTTPS) a postupně přidávejte další vrstvy:
// MauiProgram.cs — kompletní bezpečnostní registrace
public static class MauiProgram
{
public static MauiApp CreateMauiApp()
{
var builder = MauiApp.CreateBuilder();
builder
.UseMauiApp<App>()
.UseBiometricAuthentication()
.ConfigureFonts(fonts =>
{
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
});
// Bezpečnostní služby
builder.Services.AddSingleton<ITokenService, TokenService>();
builder.Services.AddSingleton<IBiometricService, BiometricService>();
builder.Services.AddTransient<AuthenticatedHttpHandler>();
// HTTP klient s certificate pinningem a auth handlerem
builder.Services.AddHttpClient("Api", client =>
{
client.BaseAddress = new Uri(AppConfig.ApiBaseUrl);
})
.AddHttpMessageHandler<AuthenticatedHttpHandler>()
#if !DEBUG
.ConfigurePrimaryHttpMessageHandler<CertificatePinningHandler>();
#else
;
#endif
// ViewModely a stránky
builder.Services.AddTransient<LoginViewModel>();
builder.Services.AddTransient<LoginPage>();
return builder.Build();
}
}
Často kladené otázky (FAQ)
Je SecureStorage v .NET MAUI skutečně bezpečný?
SecureStorage využívá nativní šifrovací mechanismy každé platformy — EncryptedSharedPreferences (AES-256 GCM) na Androidu, Keychain na iOS a DataProtectionProvider na Windows. Pro většinu aplikací je to naprosto dostatečná úroveň zabezpečení. Mějte ale na paměti omezení: na rootnutých zařízeních může být šifrování obejito a Android Auto Backup může způsobit problémy při obnově dat na novém zařízení (viz sekce výše).
Jak implementovat biometrické přihlášení v .NET MAUI?
Nejjednodušší cestou je balíček Plugin.Maui.Biometric. Nainstalujte NuGet, zaregistrujte v MauiProgram.cs pomocí .UseBiometricAuthentication(), přidejte oprávnění pro Android (USE_BIOMETRIC) a iOS (NSFaceIDUsageDescription) a poté volejte AuthenticateAsync(). Plugin podporuje otisk prstu, Face ID i Windows Hello.
Jaký je rozdíl mezi certificate pinning a běžným HTTPS?
Běžné HTTPS ověřuje, že certifikát serveru je vydaný důvěryhodnou certifikační autoritou (CA). Certificate pinning jde dál — ověřuje, že certifikát odpovídá konkrétnímu veřejnému klíči, který vaše aplikace zná. To chrání proti útokům, kdy útočník získá platný certifikát od jiné CA nebo kompromituje existující CA. V praxi to znamená výrazně vyšší úroveň ochrany, i když za cenu trochu náročnější údržby při rotaci certifikátů.
Jak bezpečně uchovávat API klíče v .NET MAUI aplikaci?
Upřímně, nejbezpečnější přístup je API klíče do aplikace vůbec nezabalovat. Místo toho vytvořte vlastní backend jako proxy — vaše aplikace se autentizuje vůči backendu a ten přidává API klíče k požadavkům na třetí strany. Pokud musíte mít v aplikaci konfigurační hodnoty, použijte build proměnné a podmíněnou kompilaci, nikdy hardcoded konstanty ve zdrojovém kódu.
Má smysl detekovat root/jailbreak na zařízení uživatele?
Pro aplikace pracující s vysoce citlivými daty (bankovnictví, zdravotnictví) rozhodně ano. Ale buďte realisté — žádná detekce není stoprocentní a zkušený útočník ji může obejít. Proto by detekce root/jailbreak měla být jednou vrstvou v širší bezpečnostní strategii, ne jediným obranným mechanismem. Uživatele informujte o riziku a zvažte omezení citlivých funkcí na kompromitovaných zařízeních.