Kódování, hashování a šifrování jsou tři různé věci

Čím se kódování, hashování a šifrování liší, k čemu Base64, procentové kódování a escapování v HTML vlastně slouží a jak neznámý řetězec poznat na první pohled.

Řetězec, který vypadá zpřeházeně, není řetězec, který je chráněný. Nečitelný výstup vyrábějí tři samostatné operace a jejich záměna je zdrojem skutečných bezpečnostních chyb.

Vratné?Potřebuje klíč?K čemu slouží
KódováníAno, komukoliNeProtáhnout bajty kanálem, který je surové neunese
HashováníNeNeOtisky, kontroly integrity, vyhledávací klíče
ŠifrováníAno, s klíčemAnoUtajení

Kódování je změna abecedy. Base64, procentové kódování, hexadecimální zápis a HTML entity zapisují tutéž informaci jinak, aby se jí parser dál v řadě nezakuckal. Žádné tajemství v tom není, takže není co střežit. Patří sem i Caesarův posun: nástroj Caesar Cipher projde hrubou silou všech 25 posunů najednou, což je poctivé shrnutí toho, jak moc pevná substituce chrání. Hashování mapuje libovolný vstup na otisk pevné délky a nedá se pustit pozpátku. Šifrování je z těch tří jediné, které poskytuje důvěrnost, a vždy v něm figuruje klíč. Pokud nedokážete ukázat na klíč, nic se nešifruje.

Base64 a co stojí

Base64 rozdělí tři bajty (24 bitů) do čtyř skupin po šesti a každou skupinu zapíše jedním znakem z 64znakové abecedy. Čtyři znaky na tři bajty znamenají, že je výstup o třetinu větší. Nejvíc to hraje roli při vkládání souborů: nástroj File to Base64 vytváří data URL, takže obrázek o 300 KB skončí ve vašem HTML jako zhruba 400 KB, které se stahují znovu s každou stránkou.

Když vstup není násobkem tří bajtů, doplní se poslední skupina znaky =:

"abc"  ->  YWJj      (3 bytes, no padding)
"ab"   ->  YWI=      (2 bytes, one =)
"a"    ->  YQ==      (1 byte, two =)

Standardní abeceda končí znaky + a /, což je v URL problém, protože + se ve formulářových datech dekóduje jako mezera a / je oddělovač cesty. Existuje proto druhá abeceda: Base64url zaměňuje + za - a / za _ a doplňkové znaky obvykle vynechává. Proto token zkopírovaný z URL někdy v dekodéru nastaveném na standardní abecedu selže. Base64 Encoder zvládá obojí a správně zachází s Unicode, což naivní implementace často nedělají: text se musí před krokem Base64 zakódovat do UTF-8.

Nic z toho není bezpečnostní opatření. Hodnota, kterou produkt v URL nazývá „zašifrovaným“ identifikátorem, je velmi často Base64 obyčejného celého čísla, které kdokoli během několika sekund přečte, změní a zakóduje zpátky. Totéž platí pro payload JSON Web Tokenu: JWT Decoder ho přečte bez klíče, protože není co odemykat.

Procentové kódování a kterou funkci použít

Procentové kódování nahrazuje bajt znakem % a dvěma hexadecimálními číslicemi. Pracuje s bajty, ne se znaky, takže se text mimo ASCII nejprve zakóduje do UTF-8: z é se stane %C3%A9.

Nevyhrazené znaky (A-Z, a-z, 0-9, -, ., _, ~) se kódovat nikdy nemusí. Vyhrazené znaky (: / ? # [ ] @ ! $ & ' ( ) * + , ; =) jsou oddělovače, které dávají URL strukturu, a jestli je znak potřeba zakódovat, závisí na tom, zda se používá jako oddělovač, nebo jako data. V tom je rozdíl mezi dvěma funkcemi JavaScriptu:

encodeURI("https://ex.com/s?q=cats & dogs")
// https://ex.com/s?q=cats%20&%20dogs   the & still splits the query

encodeURIComponent("cats & dogs")
// cats%20%26%20dogs                    safe as a single value

encodeURI slouží pro celou URL, kterou chcete udělat legální, takže oddělovače nechává být. encodeURIComponent slouží pro jednu hodnotu mířící do segmentu cesty nebo do parametru dotazu, takže je zakóduje. Pro cokoli, co vkládáte, použijte tu druhou, a počítejte s tím, že !, ', (, ) a * stejně nechá být.

A pak je tu znaménko plus. Těla formulářů posílaná jako application/x-www-form-urlencoded kódují mezeru jako + a tato konvence prosákla do dotazovacích řetězců, takže většina serverů dekóduje + v dotazu jako mezeru. Doslovné plus se musí poslat jako %2B, a proto alex+billing@example.com tak často dorazí jako alex billing@example.com se zničeným štítkem. V segmentu cesty je naproti tomu + prostě plus a mezera je %20. URL Encoder to zviditelní, když hodnota jednu cestu tam a zpět přežije a při další se rozbije.

Escapování závisí na tom, kam hodnota míří

Neexistuje jediná podoba řetězce „escapovaná pro web“. Rozhoduje kontext.

KontextSprávné zacházení
Text v HTMLEntity pro &, < a >
Uvozený atributEntity pro & a pro tu uvozovku, která ho obaluje, a atribut vždy uvozujte
Uvnitř <script>Escapování řetězců JavaScriptu, ne entity
URL v href nebo srcHodnotu procentově zakódujte, escapujte pro atribut a pak zkontrolujte schéma
Hodnota v CSSEscapování CSS a nikdy nestavte url() ze vstupu od uživatele

Případ se skriptem lidi překvapuje. Obsah skriptu se z entit nedekóduje, takže tam entita zůstane doslovná, a parser HTML blok uzavře na sekvenci </script, ať se objeví kdekoli, uvnitř uvozeného řetězce nebo ne:

<script>var s = "</script>";</script>   <!-- block ends early -->
<script>var s = "<\/script>";</script>  <!-- correct -->

Ani u URL samotné escapování nestačí. Hodnota začínající na javascript: se po kliknutí spustí, ať byla zakódovaná sebepečlivěji, takže schéma ověřte proti seznamu povolených. Nástroj HTML Entities pokrývá případ textu i atributu a entity také dekóduje zpět, což je obvyklá potřeba, když API něco dvakrát escapovalo do &amp;amp;.

Hashování: který algoritmus a na co

AlgoritmusOtiskStav
MD5128 bitůProlomený. Kolize jsou triviální
SHA-1160 bitůProlomený. Kolize se zvoleným prefixem jsou proveditelné a levné
SHA-256, SHA-512256, 512 bitůV pořádku pro integritu a podpisy
bcrypt, scrypt, Argon2různěJen hesla

MD5 a SHA-1 selhávají v odolnosti proti kolizím, což znamená, že útočník dokáže sestrojit dva různé vstupy se stejným otiskem. Všude, kde hash zastupuje dokument, jsou nepoužitelné: podpisy, certifikáty, adresování podle obsahu, odstraňování duplicit u čehokoli, co dodá útočník. Ani jeden není prolomený vůči hledání vzoru, takže starý kontrolní součet MD5 stále odhalí náhodné poškození, ale ani jeden nepatří do nové práce. Použijte SHA-256, který Hash Generator počítá pro soubory i pro text.

Hesla jsou jiný problém a rychlý hash je špatný nástroj právě proto, že je rychlý. Běžný hardware zvládne miliardy operací SHA-256 za sekundu, takže uniklá tabulka hashů hesel v SHA-256 je uniklá tabulka hesel. bcrypt, scrypt a Argon2id jsou záměrně pomalé a laditelné, s pracovním faktorem (a u těch dvou posledních i s paměťovou náročností), který zvyšujete, jak se hardware zlepšuje, a každý z nich ukládá do svého výstupu jedinečnou náhodnou sůl.

Hash také není podpis. Pro důkaz, že zpráva pochází od někoho, kdo drží klíč, použijte HMAC-SHA-256, ne hash tajemství slepeného se zprávou, který je zranitelný vůči prodloužení délky.

Co hash nedokáže

Hash nelze obrátit výpočtem. Lze ho obrátit hledáním, kdykoli je množina možných vstupů dost malá na to, aby se dala vyjmenovat: zahashujte každý čtyřmístný PIN a máte všech deset tisíc otisků okamžitě, a totéž platí pro každé PSČ, telefonní číslo nebo adresu z uniklého seznamu. Zahashování identifikátoru z něj nedělá anonymní údaj.

Solení, tedy jedinečná náhodná hodnota uložená u každého záznamu a přimíchaná do vstupu, neztíží uhodnutí jednoho cíle, ale nutí útočníka útočit na každý záznam zvlášť a předpočítané tabulky činí bezcennými. U hodnot s nízkou entropií je tou částí, která skutečně pomůže, pepř (tajný klíč držený mimo databázi).

Jak poznat neznámý řetězec

TvarPoznávací znameníPříklad
HexJen 0-9a-f, sudá délka. 32 znaků je MD5, 40 je SHA-1, 64 je SHA-2565d41402abc4b2a76b9719d911017c592
Base64Smíšená velikost písmen se znaky + a /, délka je násobkem 4, může končit na = nebo ==SGVsbG8sIHdvcmxkIQ==
Base64urlTotéž se znaky - a _, obvykle bez doplněníeyJzdWIiOiIxMjM0NSJ9
Procentové kódování% následované dvěma hexadecimálními číslicemia%20b%26c
JWTTři bloky Base64url oddělené tečkami, začíná na eyJeyJhbGciOi...
bcryptPřesně 60 znaků, začíná na $2a$, $2b$ nebo $2y$ následované cenou$2b$12$...
Argon2$argon2id$v=19$m=...,t=...,p=...$salt$hash

eyJ stojí za zapamatování: je to Base64 řetězce {", takže každý blok, který takhle začíná, je zakódovaný objekt JSON, ať už jde o token, nebo ne.

Úspěšné dekódování není důkazem, že byl odhad správný, protože Base64 promění na bajty jakýkoli vstup. Ověřte, že je výsledek věrohodný text, nebo že začíná známou signaturou souboru: Base64 začínající na iVBORw0KGgo je PNG, /9j/ je JPEG, JVBERi0 je PDF a UEsDB je zip. Pokud bajty nevypadají jako nic, prožeňte výstup nástrojem Text to Binary a přečtěte si kódy přímo, nebo zkontrolujte, jestli hodnota nebyla procentově zakódovaná ještě před zakódováním do Base64, což je běžné dvojité zabalení.