Archive

Kategorien

Starke Passwortfunktion mit scrypt

Passworte haben einen entscheidenden Nachteil: die User können sie sich kaum merken und verwenden daher oft zu einfache Passworte. Dabei spielt es dann auch keine Rolle ob aus den Passworten noch Hashes abgeleitet werden. Denn die meisten Hashfunktionen sind sehr schnell und lassen sich zudem rech „günstig“ parallelisieren d.h. durch geeignete Hardware (z.B. Grafikkarten FPU) kann man auch von der Länge her sichere Passwörter knacken. Man schaltet einfach ein paar tausend Grafikarten zusammen und lässt diese auf einen Passworthash los 🙂
Auch bei modernen Schlüsselableitungsfunktionen wie z.B. bcrypt oder PBKDF2 lassen sich die Berechnungen recht gut parallelisieren und das mit einem vertretbaren finanziellen Aufwand für die nötige Hardware. Hier kommt scrypt ins Spiel, welches sich zu nutze macht, dass RAM verhältnismässig teuer ist. Zudem nutzt scrypt Sequentialisierung um sicherzustellen, dass sich die Vorgänge nicht effizient parallelisieren lassen.

Um scrypt z.B. mit PHP zu nutzen muss man sich zuerst zwei PHP Pakete installieren. Siehe auch hier

#Fedora
yum install php-devel php-pear
#Debian
apt-get install php5-dev php-pear

danach kann man scrypt dann so installieren

pecl install scrypt

Danach holt man sich am „Einfachsten“ den scrypt Wrapper von github

<?php
/**
 * This file contains an example helper classes for the php-scrypt extension.
 *
 * As with all cryptographic code; it is recommended that you use a tried and
 * tested library which uses this library; rather than rolling your own.
 *
 * PHP version 5
 *
 * @category Security
 * @package  Scrypt
 * @author   Dominic Black <thephenix@gmail.com>
 * @license  http://www.opensource.org/licenses/BSD-2-Clause BSD 2-Clause License
 * @link     http://github.com/DomBlack/php-scrypt
 */
 
/**
 * This class abstracts away from scrypt module, allowing for easy use.
 *
 * You can create a new hash for a password by calling Password::hash($password)
 *
 * You can check a password by calling Password::check($password, $hash)
 *
 * @category Security
 * @package Scrypt
 * @author Dominic Black <thephenix@gmail.com>
 * @license http://www.opensource.org/licenses/BSD-2-Clause BSD 2-Clause License
 * @link http://github.com/DomBlack/php-scrypt
 */
abstract class Password {
 
 /**
  *
  * @var int The key length
  */
 private static $_keyLength = 32;
 
 /**
  * Get the byte-length of the given string
  *
  * @param string $str
  *         Input string
  *         
  * @return int
  */
 protected static function strlen($str) {
  static $isShadowed = null;
 
  if ($isShadowed === null) {
   $isShadowed = extension_loaded ( 'mbstring' ) && ini_get ( 'mbstring.func_overload' ) & 2;
  }
 
  if ($isShadowed) {
   return mb_strlen ( $str, '8bit' );
  } else {
   return strlen ( $str );
  }
 }
 
 /**
  * Generates a random salt
  *
  * @param int $length
  *         The length of the salt
  *         
  * @return string The salt
  */
 public static function generateSalt($length = 8) {
  $buffer = '';
  $buffer_valid = false;
  if (function_exists ( 'mcrypt_create_iv' ) && ! defined ( 'PHALANGER' )) {
   $buffer = mcrypt_create_iv ( $length, MCRYPT_DEV_URANDOM );
   if ($buffer) {
    $buffer_valid = true;
   }
  }
  if (! $buffer_valid && function_exists ( 'openssl_random_pseudo_bytes' )) {
   $cryptoStrong = false;
   $buffer = openssl_random_pseudo_bytes ( $length, $cryptoStrong );
   if ($buffer && $cryptoStrong) {
    $buffer_valid = true;
   }
  }
  if (! $buffer_valid && is_readable ( '/dev/urandom' )) {
   $f = fopen ( '/dev/urandom', 'r' );
   $read = static::strlen ( $buffer );
   while ( $read < $length ) {
    $buffer .= fread ( $f, $length - $read );
    $read = static::strlen ( $buffer );
   }
   fclose ( $f );
   if ($read >= $length) {
    $buffer_valid = true;
   }
  }
  if (! $buffer_valid || static::strlen ( $buffer ) < $length) {
   $bl = static::strlen ( $buffer );
   for($i = 0; $i < $length; $i ++) {
    if ($i < $bl) {
     $buffer [$i] = $buffer [$i] ^ chr ( mt_rand ( 0, 255 ) );
    } else {
     $buffer .= chr ( mt_rand ( 0, 255 ) );
    }
   }
  }
  $salt = str_replace ( array (
    '+',
    '$' 
  ), array (
    '.',
    '' 
  ), base64_encode ( $buffer ) );
 
  return $salt;
 }
 
 /**
  * Create a password hash
  *
  * @param string $password
  *         The clear text password
  * @param string $salt
  *         The salt to use, or null to generate a random one
  * @param int $N
  *         The CPU difficultly (must be a power of 2, > 1)
  * @param int $r
  *         The memory difficultly
  * @param int $p
  *         The parallel difficultly
  *         
  * @return string The hashed password
  */
 public static function hash($password, $salt = false, $N = 16384, $r = 8, $p = 1) {
  if ($N == 0 || ($N & ($N - 1)) != 0) {
   throw new \InvalidArgumentException ( "N must be > 0 and a power of 2" );
  }
 
  if ($N > PHP_INT_MAX / 128 / $r) {
   throw new \InvalidArgumentException ( "Parameter N is too large" );
  }
 
  if ($r > PHP_INT_MAX / 128 / $p) {
   throw new \InvalidArgumentException ( "Parameter r is too large" );
  }
 
  if ($salt === false) {
   $salt = self::generateSalt ();
  } else {
   // Remove dollar signs from the salt, as we use that as a separator.
   $salt = str_replace ( array (
     '+',
     '$' 
   ), array (
     '.',
     '' 
   ), base64_encode ( $salt ) );
  }
 
  $hash = scrypt ( $password, $salt, $N, $r, $p, self::$_keyLength );
 
  return $N . '$' . $r . '$' . $p . '$' . $salt . '$' . $hash;
 }
 
 /**
  * Check a clear text password against a hash
  *
  * @param string $password
  *         The clear text password
  * @param string $hash
  *         The hashed password
  *         
  * @return boolean If the clear text matches
  */
 public static function check($password, $hash) {
  // Is there actually a hash?
  if (! $hash) {
   return false;
  }
 
  list ( $N, $r, $p, $salt, $hash ) = explode ( '$', $hash );
 
  // No empty fields?
  if (empty ( $N ) or empty ( $r ) or empty ( $p ) or empty ( $salt ) or empty ( $hash )) {
   return false;
  }
 
  // Are numeric values numeric?
  if (! is_numeric ( $N ) or ! is_numeric ( $r ) or ! is_numeric ( $p )) {
   return false;
  }
 
  $calculated = scrypt ( $password, $salt, $N, $r, $p, self::$_keyLength );
 
  // Use compareStrings to avoid timeing attacks
  return self::compareStrings ( $hash, $calculated );
 }
 
 /**
  * Zend Framework (http://framework.zend.com/)
  *
  * @link http://github.com/zendframework/zf2 for the canonical source repository
  * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
  * @license http://framework.zend.com/license/new-bsd New BSD License
  *         
  *          Compare two strings to avoid timing attacks
  *         
  *          C function memcmp() internally used by PHP, exits as soon as a difference
  *          is found in the two buffers. That makes possible of leaking
  *          timing information useful to an attacker attempting to iteratively guess
  *          the unknown string (e.g. password).
  *         
  * @param string $expected         
  * @param string $actual         
  *
  * @return boolean If the two strings match.
  */
 public static function compareStrings($expected, $actual) {
  $expected = ( string ) $expected;
  $actual = ( string ) $actual;
  $lenExpected = static::strlen ( $expected );
  $lenActual = static::strlen ( $actual );
  $len = min ( $lenExpected, $lenActual );
 
  $result = 0;
  for($i = 0; $i < $len; $i ++) {
   $result |= ord ( $expected [$i] ) ^ ord ( $actual [$i] );
  }
  $result |= $lenExpected ^ $lenActual;
 
  return ($result === 0);
 }
}

Damit kann man sich dann ein eigenes kleines Scriptlein schreiben

<?php
#hier das Wrapper Script von oben angeben
include('scrypt-wrapper.inc');
$pass = 'Test';
$hash = '';
if ( isset( $argv[1] ) ) $pass = $argv[1];
if ( isset( $argv[2] ) ) $hash = $argv[2];
#der Wert 1048576 wird vom "Erfinder" von scrypt als sehr gut für sehr hohe Sicherheit empfohlen. Allerdings steigt auch die Rechenzeit
#fuer normale Anforderungen reicht auch ein Wert von 16384 womit die Rechenzeit deutlich sinkt
if ( !isset( $argv[2] ) ) echo Password::hash( $pass, false, 1048576 )."\n";
if ( isset( $argv[2] ) ) {
 if ( Password::check( $pass, $hash ) === true ){
  echo 'Passwort korrekt'."\n";
 } else {
  echo 'Passwort NICHT korrekt'."\n";
 }
}

Neben der Passwort Funktion gibt es unter https://www.tarsnap.com/scrypt/scrypt-1.1.6.tgz auch ein darauf basierendes Verschlüsselungstool.

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre lang="" line="" escaped="" cssfile="">

  

  

  

11 + one =

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahren Sie mehr darüber, wie Ihre Kommentardaten verarbeitet werden .