{"id":546,"date":"2011-03-17T22:17:13","date_gmt":"2011-03-17T21:17:13","guid":{"rendered":"https:\/\/d-mueller.de\/blog\/?p=546"},"modified":"2011-03-18T09:42:45","modified_gmt":"2011-03-18T08:42:45","slug":"hashverfahren-und-sicherheit","status":"publish","type":"post","link":"https:\/\/d-mueller.de\/blog\/hashverfahren-und-sicherheit\/","title":{"rendered":"Hashverfahren und Sicherheit"},"content":{"rendered":"<p>Es geh\u00f6rt mittlerweile zum guten Ton, auf <a href=\"http:\/\/de.wikipedia.org\/wiki\/MD5\">md5<\/a> herumzuhacken. Dabei f\u00e4llt immer wieder das Argument, dass in md5 Kollisionen entdeckt wurden. Eine Kollision bedeutet, dass zwei unterschiedliche Strings zum gleichen Hash f\u00fchren. Dies muss zwangsl\u00e4ufig bei allen Hashverfahren der Fall sein, denn eine Abbildung von viel Text auf wenig Text wird naturgem\u00e4\u00df irgendwann eine \u00dcberschneidung bei zwei verschiedenen Inputs ergeben. Ein gutes Hashverfahren kommt also mit weniger Kollisionen daher als ein schlechtes.<\/p>\n<p>Zum selbst testen (Unterschiede im Input sind mit einem Ausrufezeichen vermerkt):<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$a = md5(&quot;\\xA6\\x64\\xEA\\xB8\\x89\\x04\\xC2\\xAC\\x48\\x43\\x41\\x0E\\x0A\\x63\\x42\\x54\\x16\\x60\\x6C\\x81\\x44\\x2D\\xD6\\x8D\\x40\\x04\\x58\\x3E\\xB8\\xFB\\x7F\\x89\\x55\\xAD\\x34\\x06\\x09\\xF4\\xB3\\x02\\x83\\xE4\\x88\\x83\\x25\\x71\\x41\\x5A\\x08\\x51\\x25\\xE8\\xF7\\xCD\\xC9\\x9F\\xD9\\x1D\\xBD\\xF2\\x80\\x37\\x3C\\x5B\\x97\\x9E\\xBD\\xB4\\x0E\\x2A\\x6E\\x17\\xA6\\x23\\x57\\x24\\xD1\\xDF\\x41\\xB4\\x46\\x73\\xF9\\x96\\xF1\\x62\\x4A\\xDD\\x10\\x29\\x31\\x67\\xD0\\x09\\xB1\\x8F\\x75\\xA7\\x7F\\x79\\x30\\xD9\\x5C\\xEB\\x02\\xE8\\xAD\\xBA\\x7A\\xC8\\x55\\x5C\\xED\\x74\\xCA\\xDD\\x5F\\xC9\\x93\\x6D\\xB1\\x9B\\x4A\\xD8\\x35\\xCC\\x67\\xE3&quot;);\r\n$b = md5(&quot;\\xA6\\x64\\xEA\\xB8\\x89\\x04\\xC2\\xAC\\x48\\x43\\x41\\x0E\\x0A\\x63\\x42\\x54\\x16\\x60\\x6C\\x01\\x44\\x2D\\xD6\\x8D\\x40\\x04\\x58\\x3E\\xB8\\xFB\\x7F\\x89\\x55\\xAD\\x34\\x06\\x09\\xF4\\xB3\\x02\\x83\\xE4\\x88\\x83\\x25\\xF1\\x41\\x5A\\x08\\x51\\x25\\xE8\\xF7\\xCD\\xC9\\x9F\\xD9\\x1D\\xBD\\x72\\x80\\x37\\x3C\\x5B\\x97\\x9E\\xBD\\xB4\\x0E\\x2A\\x6E\\x17\\xA6\\x23\\x57\\x24\\xD1\\xDF\\x41\\xB4\\x46\\x73\\xF9\\x16\\xF1\\x62\\x4A\\xDD\\x10\\x29\\x31\\x67\\xD0\\x09\\xB1\\x8F\\x75\\xA7\\x7F\\x79\\x30\\xD9\\x5C\\xEB\\x02\\xE8\\xAD\\xBA\\x7A\\x48\\x55\\x5C\\xED\\x74\\xCA\\xDD\\x5F\\xC9\\x93\\x6D\\xB1\\x9B\\x4A\\x58\\x35\\xCC\\x67\\xE3&quot;);\r\n\/\/--------------------------------------------------------------------------------------!-------------------------------------------------------------------------------------------------------!-------------------------------------------------------!-----------------------------------------------------------------------------------------------!-------------------------------------------------------------------------------------------------------!-------------------------------------------------------!------------------\r\n\r\nvar_dump($a); \/\/string(32) &quot;2ba3be5aa541006b62370111282d19f5&quot;\r\nvar_dump($b); \/\/string(32) &quot;2ba3be5aa541006b62370111282d19f5&quot;\r\nvar_dump($a === $b); \/\/bool(true)\r\n<\/pre>\n<p>Dar\u00fcberhinaus existieren mittlerweile sogar <a href=\"http:\/\/www.mscs.dal.ca\/~selinger\/md5collision\/\">Generatoren<\/a>, die zwei v\u00f6llig verschiedenen <i>exe<\/i>-Dateien den gleichen md5-Hash verschaffen. Auch Zertifikate lassen sich f\u00e4lschen. So l\u00e4sst sich jemandem ein selbst erstelltes Zertifikat unterjubeln oder ein Virus anstelle der eigentlich erwarteten Datei schicken, ohne dass dies durch einen md5-Check auffallen w\u00fcrde.<\/p>\n<p>Okay, das sieht jetzt erstmal gemein aus &#8211; aber in wie weit stellt das <b>f\u00fcr Webanwendungen<\/b> ein Sicherheitsrisiko dar? Dazu fragen wir uns erstmal, was denn das typische Horrorszenario ist. Einem Angreifer f\u00e4llt unsere komplette Datenbank in die H\u00e4nde, in der alle (hoffentlich) gehashten Passw\u00f6rter enthalten sind. Der Angreifer versucht nun, Strings zu erzeugen, die mit dem Hash \u00fcbereinstimmen. Das kann das wirkliche Passwort sein, oder irgendein anderes, was zum gleichen Hash-Ergebnis f\u00fchrt (= Kollision). Bei einem Hashverfahren, <b>bei dem es viele Kollisionen gibt<\/b>, hat der Angreifer also weniger Arbeit beim Brute-Forcen des Passworts. Aber oft ist Bruteforce garnicht n\u00f6tig&#8230;<\/p>\n<h2>Das M\u00e4rchen mit den Rainbowtables<\/h2>\n<p>Wer reine Hashes der Form <i>md5($password)<\/i> \/ <i>sha1($password)<\/i> \/ &#8230; in der Datenbank ablegt, kann es eigentlich auch gleich sein lassen und die Passw\u00f6rter im Plaintext speichern. Gut, die Aussage ist jetzt leicht provokant, aber in Zeiten von <a href=\"http:\/\/www.md5conversion.com\/\">online reverse lookups<\/a> und <a href=\"http:\/\/www.project-rainbowcrack.com\/table.htm\">600GB gro\u00dfen Rainbowtables<\/a> f\u00fcr alle bekannten Hashverfahren bewegt man sich auf d\u00fcnnem Eis.<\/p>\n<h2>&#8230; und alle schreien nach dem salt<\/h2>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\ndefine(&quot;MY_SALT&quot;, &quot;abc123&quot;);\r\n$password = md5(MY_SALT . $_POST[&#039;password&#039;]);\r\n<\/pre>\n<p>Wer das f\u00fcr toll h\u00e4lt, h\u00e4lt sicher auch register_globals f\u00fcr eine praktische Sache. Das offensichtliche Problem mit solch einem globalen salt ist, dass es mittlerweile auch vorberechnete Rainbowtables f\u00fcr typische salts gibt. Und selbst wenn nicht, berechnen wir in Zeiten von Amazon EC2 mit GPU Rechenunterst\u00fctzung mal eben 7 Billionen Passw\u00f6rter pro Sekunde &#8211; vorausgesetzt nat\u00fcrlich, dass der Angreifer unser salt kennt. Besser ist es da schon, f\u00fcr jeden User ein eigenes salt zu verwenden:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$salt = uniqid(mt_rand(), true);\r\n$password = md5($salt . &quot;x&quot; . $userid . &quot;y&quot; . $userpassword);\r\n<\/pre>\n<p>Die userid und die zwei Buchstaben packen wir noch dazu, da ein simples konkatenieren von Passwort und salt im Stile <i>$password = md5($salt . $userpassword);<\/i> sehr voraussehbar ist. Wenn nun also dem Angreifer &#8222;blo\u00df&#8220; unsere Datenbank (als ob das nicht schon schlimm genug w\u00e4re&#8230;), nicht aber der Programmcode in die H\u00e4nde f\u00e4llt, wird er voraussichtlich dran scheitern die genaue Zusammensetzung zu rekonstruieren. Jetzt speichern wir $salt und $password f\u00fcr jeden User in der Datenbank und sind schonmal einen Schritt weiter.<\/p>\n<h2>Da geht noch mehr!<\/h2>\n<p>Das Problem mit md5 (und dem kollisionsfreieren sha1) ist, dass sie unglaublich schnell zu berechnen sind. Wenn wir also erreichen k\u00f6nnten, dass ein Angreifer pro Sekunde nicht 7 Billionen, sondern nur 3 Hashes erstellen kann, w\u00e4ren wir einen weiteren gro\u00dfen Schritt gekommen. Dazu k\u00f6nnen wir uns einen simplen Loop basteln:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\ndefine(&quot;ITERATIONS&quot;, 400000);\r\n$salt = uniqid(mt_rand(), true);\r\n$password = md5($salt . &quot;x&quot; . $userid . &quot;y&quot; . $userpassword);\r\n\r\nfor ($i = 0; $i &lt; ITERATIONS; $i++)\r\n{\r\n\t$password = md5($password);\r\n}\r\n<\/pre>\n<p>Wenn dem Angreifer nun also zus\u00e4tzlich zur Datenbank auch noch unser Quellcode in die H\u00e4nde f\u00e4llt, hat er trotzdem nicht viel gewonnen. Schlie\u00dflich muss er f\u00fcr jedes Passwort eine eigene Rainbow-Tabelle anhand des einzigartigen salts erstellen und leidet zus\u00e4tzlich noch f\u00fcrchterlich durch die langsame Berechnungsdauer aufgrund der vielen Iterationen. Wenn man noch einen draufsetzen will, k\u00f6nnte man eine leicht variierende Iterationsanzahl verwenden und diese analog zum salt zus\u00e4tzlich in der Datenbank mit speichern, etwa so:<\/p>\n<pre data-enlighter-language=\"php\" class=\"EnlighterJSRAW\">\r\n$iterations = rand(300000, 500000);\r\n<\/pre>\n<p>Achja, in diesem Zusammenhang erw\u00e4hnt sei auch &#8222;<a href=\"http:\/\/stackoverflow.com\/questions\/348109\/is-double-hashing-a-password-less-secure-than-just-hashing-it-once\">Is \u201cdouble hashing\u201d a password less secure than just hashing it once?<\/a>&#8222;.<\/p>\n<h2>Letzte Worte<\/h2>\n<p>Abschlie\u00dfend m\u00f6chte ich noch den tollen Artikel <a href=\"http:\/\/www.unixwiz.net\/techtips\/iguide-crypto-hashes.html\">An Illustrated Guide to Cryptographic Hashes<\/a> empfehlen und auf die PHP-Funktion <a href=\"http:\/\/php.net\/manual\/de\/function.crypt.php\">crypt<\/a> hinweisen (siehe auch den Wikipedia-Artikel zur darunterliegenden <a href=\"http:\/\/en.wikipedia.org\/wiki\/Crypt_(Unix)\">Unix-Crypt-Funktion<\/a>). Hierbei wird das salten und iterieren direkt von der Funktion \u00fcbernommen. Da allerdings als Hashverfahren bisher nur DES, Blowfish und MD5 unterst\u00fctzt werden und ich das Handling der Funktion als sehr m\u00fchselig empfinde, spreche ich hier keine Empfehlung aus.<\/p>\n<p>Um das oben angesprochene md5-Bashing nochmal aufzugreifen: Ich glaube, dass die Wahl des Hashverfahrens verglichen mit den sonstigen &#8222;Worst Practices&#8220; wie das Versenden von Passw\u00f6rtern per Email oder die Speicherung im Cookie eine eher untergeordnete Rolle einnnimmt. Nichtsdestotrotz: Da es das bessere sha1 gibt, kann man es nat\u00fcrlich verwenden. Achja, der \u00fcbliche Disclaimer bei solchen Themen: <b>Ich bin kein Kryptographie-Experte<\/b>, sonden k\u00e4ue nur Meinungen bzw. Erfahrungen von mir und anderen wieder ;).<\/p>\n<p>Wie geht ihr mit dem Thema um?<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Es geh\u00f6rt mittlerweile zum guten Ton, auf md5 herumzuhacken. Dabei f\u00e4llt immer wieder das Argument, dass in md5 Kollisionen entdeckt wurden. Eine Kollision bedeutet, dass zwei unterschiedliche Strings zum gleichen Hash f\u00fchren. Dies muss zwangsl\u00e4ufig bei allen Hashverfahren der Fall &hellip; <a href=\"https:\/\/d-mueller.de\/blog\/hashverfahren-und-sicherheit\/\">Weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4,6,3],"tags":[],"class_list":["post-546","post","type-post","status-publish","format-standard","hentry","category-php","category-security","category-webdev"],"_links":{"self":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/546","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/comments?post=546"}],"version-history":[{"count":0,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/posts\/546\/revisions"}],"wp:attachment":[{"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/media?parent=546"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/categories?post=546"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/d-mueller.de\/blog\/wp-json\/wp\/v2\/tags?post=546"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}