Následný postup, jak bezpečně ukládat tokeny v systému Android

Jako prolog k tomuto článku chci poznamenat krátkou větu pro pomyslného čtenáře. Tato nabídka bude důležitá, když se posuneme kupředu.

Absolutní bezpečnost neexistuje. Bezpečnost je soubor opatření, která se hromadí a kombinují a snaží se nevyhnutelné zpomalit.

Téměř před třemi lety jsem napsal příspěvek poskytující několik nápadů, jak chránit řetězce String před hypotetickým útočníkem, který dekompiluje naši aplikaci pro Android. Kvůli zapamatování a za účelem odvrácení nevyhnutelné smrti internetu zde reprodukuji některé oddíly.

Jeden z nejčastějších případů použití nastane, když naše aplikace potřebuje komunikovat s webovou službou za účelem výměny dat. Tato výměna dat se může pohybovat od méně citlivější povahy a může se lišit mezi žádostí o přihlášení, peticí o změně uživatelských údajů atd.

Absolutním prvním opatřením, které se má použít, je připojení SSL (Secure Sockets Layer) mezi klientem a serverem. Jděte znovu k původní nabídce. To nezajišťuje absolutní soukromí a bezpečnost, i když to dělá dobrou počáteční práci.

Pokud používáte připojení SSL (jako když v prohlížeči uvidíte skříňku), znamená to, že spojení mezi vámi a serverem je šifrováno. Na teoretické úrovni nemůže nic získat přístup k informacím obsaženým v těchto požadavcích (*)

(*) Zmínil jsem se, že absolutní zabezpečení neexistuje? Připojení SSL mohou být stále ohrožena. Tento článek nemá v úmyslu poskytnout rozsáhlý seznam všech možných útoků, ale chci vás informovat o několika možnostech. Lze použít falešné SSL certifikáty i útoky typu Man-in-the-Middle.

Pojďme kupředu. Předpokládáme, že náš klient komunikuje prostřednictvím šifrovaného kanálu SSL s naší backend. Vyměňují si užitečná data, dělají z jejich podnikání radost. Chceme však poskytnout další vrstvu zabezpečení.

Dalším logickým krokem, který se dnes používá, je poskytnout autentizační token nebo klíč API, který se použije v komunikaci. Funguje to takto. Náš backend dostává petici. Jak víme, že petice pochází od jednoho z našich ověřených klientů, a ne náhodného chlapa, který se pokouší získat přístup k našemu API? Backend zkontroluje, zda klient poskytuje platný klíč API. Pokud je předchozí prohlášení pravdivé, pokračujeme v žádosti. V opačném případě to popíráme a v závislosti na povaze našeho podnikání přijímáme některá nápravná opatření (když se to děje, obzvláště rád ukládám IP a ID od klienta, abych viděl, jak často k tomu dochází. Když frekvence stoupá více než je to žádoucí pro mou jemnou chuť, zvažuji zákaz nebo pozorně sleduji, čeho se nedovolený internetový kámoš snaží dosáhnout).

Postavme náš hrad ze země. V naší aplikaci pravděpodobně přidáme proměnnou nazvanou API_KEY, která se automaticky vloží do každého požadavku (pokud používáte Android, pravděpodobně v klientovi Retrofit).

soukromý finální statický řetězec API_KEY = “67a5af7f89ah3katf7m20fdj202”

To je skvělé a funguje to, pokud chceme klienta ověřit. Problém je v tom, že sám o sobě neposkytuje velmi účinnou vrstvu.

Použijete-li apktool k dekompilaci aplikace a provedení hledání hledajícího řetězce, najdete v jednom z výsledných souborů .smali následující:

const-string v1, „67a5af7f89ah3katf7m20fdj202“

Ano jistě. Neříká se, že se jedná o ověřovací token, takže stále musíme projít pečlivým ověřením, abychom se rozhodli, jak dosáhnout tohoto řetězce a zda jej lze použít pro účely ověřování. Ale víte, kam jdu: je to většinou otázka času a zdrojů.

Může nám program Proguard pomoci zajistit tento řetězec, takže se tím nemusíme starat? Spíš ne. Proguard ve své FAQ uvádí, že String šifrování není úplně možné.

A co uložení tohoto řetězce do jednoho z dalších mechanismů poskytovaných systémem Android, jako je SharedPreferences? To je sotva dobrý nápad. SharedPreferences lze snadno získat z emulátoru nebo z jakéhokoli zakořeněného zařízení. Před několika lety chlap jménem Srinivas dokázal, jak by se skóre mohlo ve videohře změnit. Docházejí nám možnosti!

Souprava pro nativní vývoj (NDK)

Budu zde aktualizovat původní model, který jsem navrhl, a jak to můžeme také iterovat, abychom zajistili bezpečnější alternativu. Představme si dvě funkce, které by mohly posloužit k šifrování a dešifrování našich dat:

Nic fantastického zde. Tyto dvě funkce vezmou hodnotu klíče a řetězec, který se má kódovat nebo dekódovat. Vrátí zašifrovaný nebo dešifrovaný token. Následující funkci bychom nazvali takto:

Hádáte směr? To je správně. Na požádání jsme mohli šifrovat a dešifrovat náš token. To poskytuje další vrstvu zabezpečení: když je kód zmatený, není již tak přímočarý jako provádění vyhledávání v řetězci a kontrola prostředí obklopujícího tento řetězec. Ale můžete stále přijít na problém, který je třeba vyřešit?

Můžeš?

Pokud jste to ještě neudělali, dejte mu ještě pár vteřin.

Ano, máš pravdu. Máme šifrovací klíč, který je také uložen jako řetězec. Toto přidává další vrstvy zabezpečení podle nejasností, ale stále máme token na prostém textu, bez ohledu na to, zda je tento token použit pro šifrování nebo je token per-se.

Nyní použijeme NDK a budeme opakovat náš bezpečnostní mechanismus.

NDK nám umožňuje přístup k základně kódu C ++ z našeho kódu Android. Jako první přístup si chvilku pomyslíme, co dělat. Mohli bychom mít nativní funkci C ++, která ukládá klíč API nebo jakákoli citlivá data, která se pokoušíme uložit. Tato funkce by mohla být později vyvolána z kódu a žádný řetězec nebude uložen v žádném Java souboru. To by zajistilo automatickou ochranu proti technikám dekompilace.

Naše funkce C ++ by vypadala následovně:

V Java kódu se snadno nazývá:

A funkce šifrování / dešifrování bude vyvolána jako v následujícím fragmentu:

Pokud víme, že vygenerujeme APK, zmatíme ho, dekompilujeme ho a pokusíme se získat přístup k řetězci obsaženému v nativní funkci getSecretKey (), nebudeme ho moci najít! Vítězství?

Spíš ne. NDK kód může být ve skutečnosti rozebrán a zkontrolován. To se zhoršuje a začínáte vyžadovat pokročilejší nástroje a techniky. Zbavili jste se 95% dětí ve skriptu, ale tým s dostatečnými zdroji a motivací bude mít stále přístup k tokenu. Pamatujete si tuto větu?

Absolutní bezpečnost neexistuje. Bezpečnost je soubor opatření, která se hromadí a kombinují a snaží se nevyhnutelné zpomalit.
Stále máte přístup v rozebraném kódu String literály!

Například Hex Rays dělá velmi dobrou práci při dekompilaci nativních souborů. Jsem si jist, že existuje spousta nástrojů, které by mohly dekonstruovat jakýkoli nativní kód vygenerovaný pomocí Androidu (nejsem spojen s hexadecimálními paprsky ani od nich nedostává žádnou peněžní kompenzaci).

Jaké řešení bychom mohli použít ke komunikaci mezi backendem a klientem, aniž bychom byli označeni?

Generujte klíč v reálném čase na zařízení.

Vaše zařízení nemusí ukládat žádný klíč a musí se vypořádat se všemi problémy s ochranou literálu String! Toto je velmi stará technika používaná službami, jako je vzdálené ověření klíče.

  1. Klient zná funkci (), která vrací klíč.
  2. Backend zná funkci () implementovanou v klientovi
  3. Klient generuje klíč prostřednictvím funkce () a ten je doručen na server.
  4. Server to ověří a pokračuje s požadavkem.

Spojujete tečky? Namísto nativní funkce, která vám vrací řetězec (snadno identifikovatelný), proč nemít funkci, která vám vrací součet tří náhodných prvočísel mezi 1 a 100? Nebo funkce, která bere aktuální den vyjádřený v unixime a přidává 1 ke každé jiné číslici? A co odebrání některých kontextuálních informací ze zařízení, jako je množství použité paměti, k zajištění vyššího stupně entropie?

Poslední odstavec obsahuje řadu nápadů, ale náš hypotetický čtenář snad zaujal hlavní bod.

souhrn

  1. Absolutní bezpečnost neexistuje.
  2. Klíčem k dosažení vysokého stupně bezpečnosti je kombinace souboru ochranných opatření.
  3. Do kódu neskladujte literály String.
  4. Pomocí NDK vytvořte vlastní generovaný klíč.

Pamatujete si první větu?

Absolutní bezpečnost neexistuje. Bezpečnost je soubor opatření, která se hromadí a kombinují a snaží se nevyhnutelné zpomalit.

Chci ještě jednou zdůraznit, že vaším cílem je co nejvíce chránit váš kód, aniž bychom ztratili perspektivu, že 100% zabezpečení je nedosažitelné. Pokud však dokážete svůj kód chránit způsobem, který vyžaduje dešifrování veškerých rozumných informací, které vyžaduje velké množství zdrojů, budete moci dobře a klidně spát.

Malý zřeknutí se odpovědnosti

Vím. Dostal jsi se sem a přemýšlel v celém článku „jak tenhle chlap nezmiňuje Dexguarda a prochází všemi problémy?“. Máš pravdu. Dexguard může skutečně zatemňovat Struny a dělají na tom velmi dobrou práci. Ceny společnosti Dexguard však mohou být nepřípustné. Dexguard jsem použil v předchozích společnostech s kritickými zabezpečovacími systémy, ale to nemusí být pro každého. A ve vývoji softwaru i v životě, čím více možností máte, tím bohatší a bohatší je svět.

Šťastný kódování!

Píšu své myšlenky o softwarovém inženýrství a životě obecně na svém Twitteru. Pokud se vám tento článek líbil nebo vám to pomohlo, klidně se o něj podělte, ♥ to a / nebo napište komentář. To je měna, která pohání amatérské spisovatele.