Bezpečnost 2. – HTTPS

Minule jsme si ukázali, že keychain není zrovna nejbezpečnější místo ve vašem telefonu. Dnes si rozebereme HTTPS a podíváme se na poměrně časté chyby.

Ověřování SSL certifikátu

Jak funguje? Operační systém, v našem případě iOS, má několik kořenových certifikátů, kterým důvěřuje a považuje je za důvěryhodné. Při navazování bezpečného spojení získává aplikace informace o certifikátu na serveru a k tomu celý řetězec certifikátů, který by měl vést až k jednomu z kořenových certifikátů. Ověřit si to můžete například u idmsa.apple.com a to pomocí:

openssl s_client -showcerts -connect idmsa.apple.com:443

Zkrácený výstup:

Certificate chain
 0 s:CN=idmsa.apple.com
   i:CN=VeriSign Class 3 Extended Validation SSL SGC CA
 1 s:CN=VeriSign Class 3 Extended Validation SSL SGC CA
   i:CN=VeriSign Class 3 Public Primary Certification Authority - G5
 2 s:CN=VeriSign Class 3 Public Primary Certification Authority - G5
   i:OU=Class 3 Public Primary Certification Authority

Server certificate
subject=CN=idmsa.apple.com
issuer=VeriSign Class 3 Extended Validation SSL SGC CA

Pokud celý řetězec vede až k jednomu z důvěryhodných kořenových certifikátů, je vše v pořádku. Jenže praxe bývá odlišná.

Vypnutí validace

Nikdo nevyvíjí proti produkčním serverům a na testovacích serverech jsou z velké části certifikáty, které neprojdou validací. Tzn. vlastní certifikáty, které nejsou podepsané důvěryhodnou certifikační autoritou. Potom nám všechny spojení padnou s chybou An SSL error has occurred and a secure connection to the server cannot be made.

Člověk je tvor líný, nechce řešit certifikáty a tak do zdrojáku dá něco takového:

AFSecurityPolicy *securityPolicy = [[AFSecurityPolicy alloc] init];   
[securityPolicy setAllowInvalidCertificates:YES];
AFHTTPClient *client = ...;
[client setSecurityPolicy:securityPolicy];

Vypnutí validace se nepoužívá jenom v případě certifikátů, které jsme si sami podepsali. Můžeme mít certifikát podepsaný důvěryhodnou autoritou, ale chceme si odladit komunikaci aplikace se serverem, podívat se co přesně si posíláme. Například pomocí Charlese. Ten umí SSL proxy, on-the-fly generuje certifikáty a ukazuje mi celou komunikaci. Podstrčí aplikaci svůj certifikát, aby mohl data dešifrovat a ten certifikát je pro iOS nevalidní. Co potom udělám? Vypnu validaci.

Další možností je přidání vlastních certifikátů jako důvěryhodné, kořenové, pomocí SecTrustCopyAnchorCertificates, SecTrustCreateWithCertificates a SecTrustSetAnchorCertificates.

To všechno Apple pěkně popisuje v dokumentu Overriding TLS Chain Validation Correctly.

Pro vývoj si validaci vypnu a pro produkci zapnu. V čem je problém? Ve způsobu jak je to udělané. Obvykle to vývojáři odněkud zkopírují, vloží do kódu a zapomenou na to. Nebo k tomu dají komentář FIXME, TODO a nikdy se na ty komentáře nepodívají. Před vydáním produkční verze je stres, hledají se poslední chyby a na podobné věci se zapomíná = zůstávají v aplikaci. Omylem, ale zůstávají.

Pokud budete vypínat validaci, zkuste to automatizovat. Já osobně mám více targetů v projektu. Jsou to ty samé aplikace, ale s různým nastavením. V kódu to většinou řídím pomocí maker, tj. něco jako:

#if APP_DEV
// vypnutí validace
#endif

Když potom dojde na automatizaci, continuous integration, … do produkce se to nedostane. Nemusím na to myslet.

SSL Pinning

Máme vyřešené certifikáty, jsou podepsané důvěryhodnou certifikační autoritou a co dál? Systém nás nechá komunikovat se serverem, ale jak víme, že je to opravdu náš server? Na to se používá SSL Pinning. Jednoduše řečeno, aplikace si sebou nese certifikát, který je na serveru a při navazování spojení kontroluje, že na druhé straně je opravdu server s tím správným certifikátém.

Pokud používáte AFNetworking, mrkněte znovu na AFSecurityPolicy a metody:

Chcete-li si to udělat sami, inspirujte se v -(BOOL)evaluateServerTrust:forDomain:. Kontrolu provedete v -connection:willSendRequestForAuthenticationChallenge:, jak Apple popisuje zde. Příkladů najde na internetu poměrně dost.

Ale … Vyvarujte se pouhého kopírování a vkládání do kódu. Vždy si ten kód prostudujte a podívejte se, jestli opravdu dělá to co slibuje.

Má to jednu nevýhodu. Certifikáty, které jsou na serverech, mají relativně krátkou platnost. A tak musíte před vypršením platnosti vydat novou verzi aplikace, která už bude obsahovat i nový certifikát. Jinak vám aplikace přestane fungovat v den vypršení platnosti certifikátu. Dá se to řešit i tak, že se nekontroluje přímo certifikát serveru, ale ten, kterým je podepsán. Ty mají delší platnost. Ale to je v některých případech nežádoucí.

Podepisování requestů

V AppStore najdete aplikace, které mají vypnutou validaci a nebo nemají ani SSL Pinning. To ale neznamená, že se po nich musíme opičit. Když už nic jiného, zkontrolujte si, že nemáte vypnutou validaci a implementujte SSL Pinning.

Můžeme dál zvýšit bezpečnost? Zabránit replay útokům? Můžeme. Například podepisováním requestů. Podepsat bychom měli všechny důležité informace a zahrnout do nich náhodný prvek.

Jak to udělat? Způsobů je mnoho. Ukážeme si jeden z nich. Vždy podepisujeme předem definovaný formát zprávy, která obvykle obsahuje:

  • nonce,
  • cestu z URL,
  • query z URL a
  • body HTTP requestu.

Dále si definujeme jak to budeme podepisovat (algoritmus, …). Řekněme, že chceme HMAC-SHA1 s předem definovaným klíčem. Tento klíč má uložená aplikace a server. A protože používáme AFNetworking, komunikujeme pomocí JSONu, uděláme si vlastní request serializer, který bude dědit z AFJSONRequestSerializer. Ukázková implementace jako gist.

A to je celé. Všechny requesty ze strany aplikace se nám automaticky podepisují. Na straně serveru implementujeme stejný algoritmus a kontrolujeme:

  • unikátnost nonce, pokud není, request ignorujeme (server si použité nonce ukládá),
  • serverem vygenerovaná hodnota signature odpovídá tomu co poslal klient, pokud ne, request se ignoruje.

UPOZORNĚNÍ: Nekopírujte tento kód do své aplikace. Je to jenom nástřel implementace, toho, jak by to mohlo vypadat. Naimplementovat si to musíte sami a hlavně nikdy, ale nikdy, nezveřejňujte algoritmus (jak skládáte zprávu pro podpis, jak podepisujete zprávu, …).

Pokud vše výše uvedené implementujete, nikde neuděláte chybu, jste v relativním bezpečí. Bráníte replay útokům, kontrolujete SSL certifikát, …

Ale nenechte se ukolébat, je to jenom jedna vrstva. Problémy číhají i jinde.

ios-ssl-kill-switch

Když budu mít zájem o vaši aplikaci, server, s výše uvedeným nemám moc problémů. Zaplatím drobné a stáhnu si ji z AppStore. Na jednom ze svých zařízení provedu Jailbreak a nainstaluju ios-ssl-kill-switch. V systémovém nastavení mám nový přepínač, který umí vypnout validaci SSL certifikátů na úrovni systémových knihoven. Takže SSL Pinning je neaktivní, vůbec se nedostane k lizu. Vesele tak můžu sledovat co si aplikace povídá se serverem.

Co můžu udělat dál? Všechny aplikace, které pochází z AppStore jsou šifrované. Něco na způsob DRM. Takže neprovedu class dump, apod. Nevadí, protože není problém binárku dešifrovat. Proč bych to dělal ručně když mám k dispozici Clutch. A jelikož je Objective-C hacker friendly jazyk, posunu se ještě dál pomocí class-dump-z. Hned mám přehled o celé aplikaci a tak můžu hledat kde aplikace podepisuje requesty, hledat klíč, … Dejme si do ukázkové aplikace tento kód:

Název metody jsme vytipovali pomocí Clutch, class-dump-z, … Pojďme zkusit najít klíč a formát zprávy. Na jailbreaknutý telefon si nainstalujeme Cydia Substrate, Cycript a balíček syslogd to /var/log/syslog. Z gistu stáhneme test.js a spustíme testovací aplikaci (moje se jmenuje Test). Pomocí cycript se hooknem na naši metodu:

cycript -p Test test.js

V konzoli se díváme do logu pomocí:

tail -f /var/log/syslog

V aplikace vyvoláme akci, která povede k zavolání signatureWithPlainText:key: a v syslogu:

Test[2442]: Text: ->message-to-sign<-
Test[2442]: Key: ->super-secret-key<-
Test[2442]: signature: signed-message-to-sign-with-key-super-secret-key`

Máme klíč, vidíme jak vypadá podepisovaná zpráva, … Jedna z cest. Druhá může být, že si patchnu debugserver, vzdáleně se připojím z LLDB a můžu hledat dál. Breakpointy, backtrace, … A poměrně rychle se člověk dostane k tomu co potřebuje. Nepřeberné množství možností.

Proč to ukazuju? Aby bylo vidět, že existuje nespočet nástrojů, které hackerům pomáhají. Nejen jim, ale i script kiddies. Ty tomu v podstatě nerozumí, ale podle návodů zkoušejí kde co a občas na něco narazí. Primárně se tedy chráníte proti script kiddies, sekundárně proti hackerům a to je marný boj. Když bude skilled hacker chtít, stejně vás na něčem nachytá. Hlavně si čte strojový kód jako pohádku na dobrou noc a tyhle nástroje nepoužívá 🙂

Dávejte si pozor na to s kým aplikace komunikuje a kontrolujte data, která vám posílá server. Nikdy nevíte kde číhá nebezpečí. V další části si řekneme pár obecných pravidel, připravíme si nějaké zadání a začneme implementovat. A hlavně se bránit! 🙂