Strona 1 z 1

[Rozw.] PHP mysql + challenge key method

: 2013-01-09, 22:39
autor: webster
System wykorzystujący tzw. challenge key, dodawany dynamicznie do przesyłanego hasła. Oprócz tego całość jest szyfrowana skryptem MD5[...]
Ktoś widział? Ktoś ma, ktoś słyszał, o jakimś example? Połączenie php z mysql? Bardzo proszę o naprowadzenie. Szukam po necie, ale jakaś cisza, brak konkretów.

Pozdr

Re: PHP mysql + challenge key method

: 2013-01-10, 00:50
autor: mina86
Po całości cytatu („skryptem MD5”) wnioskuję, iż autor nie miał pojęcia o czym pisał. Więcej kontekstu mogłoby pomóc.

Re: PHP mysql + challenge key method

: 2013-01-10, 12:18
autor: ydoom
Może chodzi o coś takiego: http://wiki.znc.in/Autoop

Czyli klient, zamiast wysyłać do serwera hasło, wysyła md5 połączenia hasła i otrzymanego z serwera "challenge".

Wydaje mi się że jest problem z takim rozwiązaniem: hasła muszą być na serwerze pamiętane w formie niezakodowanej.

Re: PHP mysql + challenge key method

: 2013-01-10, 17:09
autor: dienet
http://en.wikipedia.org/wiki/Challenge- ... n_Protocol ?

Nie zakładajmy że to jest robione z poziomu mysql, równie dobrze, ktoś może się "bunkrować" przed połączeniem do mysql.

Re: PHP mysql + challenge key method

: 2013-01-10, 17:38
autor: webster
Chyba wiem co ten autor miał na myśli, stworzyłem coś na jego myśl:

Kod: Zaznacz cały

<html>
<head>
<script type="text/javascript" src="md5.js"></script>
<script type="text/javascript">
function szyfruj()
  {
  login = document.getElementById('login');
  haslo = document.getElementById('pass');
  klucz = document.getElementById('key');
  if(login.value == '')
    {
    alert('Musisz wypełić pole login');
    login.style.border='solid red 2px';
    login.focus();
    return false;
    }
  if(pass.value == '')
    {
    alert('Musisz wypełnić pole hasło');
    pass.style.border='solid red 2px';
    pass.focus();
    return false;
    }
  pass.value = hex_md5(pass.value);
  pass.value = hex_md5(klucz.value+hex_md5(pass.value));
  return true;
  }
</script>
<body>
<form method="post" action="" onsubmit="#">
<input type="hidden" id="key" name="klucz" value="<?php print md5(uniqid()); ?>" />
Login: <input type="text" id="login" name="login" /><br />
Haslo: <input type="password" id="pass" name="haslo" /><br />
<input type="submit" value="Sprawdz" />
</form>
<?php
$login=$_POST["login"];
$pass=$_POST["haslo"];
$klucz=$_POST["klucz"];

if(!empty($login) && !empty($pass))
print '<p>Przeslano: '.$login.'<br />'.$pass.'<br />'.$klucz.'</p>';
$pass = md5($pass);
echo "Haslo z formularza w MD5: <b>$pass</b><br />";
$pass = md5( $klucz.md5($pass));
echo "Haslo z formularza z kluczem w MD5 <b>$pass</b><br />" ;
mysql_connect('localhost','user_bazy','haslo_do_bazy');
mysql_select_db('baza');
$login = isset($_SESSION['login']) ? $_SESSION['login'] : mysql_escape_string($_POST['login']);
$haslo = mysql_escape_string(md5($_POST['pass']));
$zapytanie = mysql_query("SELECT * FROM users WHERE login = '$login'") or die(mysql_error());;
$dane = mysql_fetch_array($zapytanie);
$passbase = ''.$dane['password'].'';
echo "Pobrane haslo z bazy: <b>$passbase</b><br />" ;
$passbase = md5( $klucz.md5($passbase));
echo "Haslo z bazy z kluczem w MD5 <b>$passbase</b><br />" ;
if($pass == md5( $klucz.md5($passbase)) )
  print '<p>Hasło poprawne!</p>';
?>
</body>
</html>

md5.js

Kod: Zaznacz cały

/*
 * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
 * Digest Algorithm, as defined in RFC 1321.
 * Version 2.2 Copyright (C) Paul Johnston 1999 - 2009
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for more info.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;   /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = "";  /* base-64 pad character. "=" for strict RFC compliance   */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_md5(s)    { return rstr2hex(rstr_md5(str2rstr_utf8(s))); }
function b64_md5(s)    { return rstr2b64(rstr_md5(str2rstr_utf8(s))); }
function any_md5(s, e) { return rstr2any(rstr_md5(str2rstr_utf8(s)), e); }
function hex_hmac_md5(k, d)
  { return rstr2hex(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function b64_hmac_md5(k, d)
  { return rstr2b64(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d))); }
function any_hmac_md5(k, d, e)
  { return rstr2any(rstr_hmac_md5(str2rstr_utf8(k), str2rstr_utf8(d)), e); }

/*
 * Perform a simple self-test to see if the VM is working
 */
function md5_vm_test()
{
  return hex_md5("abc").toLowerCase() == "900150983cd24fb0d6963f7d28e17f72";
}

/*
 * Calculate the MD5 of a raw string
 */
function rstr_md5(s)
{
  return binl2rstr(binl_md5(rstr2binl(s), s.length * 8));
}

/*
 * Calculate the HMAC-MD5, of a key and some data (raw strings)
 */
function rstr_hmac_md5(key, data)
{
  var bkey = rstr2binl(key);
  if(bkey.length > 16) bkey = binl_md5(bkey, key.length * 8);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = binl_md5(ipad.concat(rstr2binl(data)), 512 + data.length * 8);
  return binl2rstr(binl_md5(opad.concat(hash), 512 + 128));
}

/*
 * Convert a raw string to a hex string
 */
function rstr2hex(input)
{
  try { hexcase } catch(e) { hexcase=0; }
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var output = "";
  var x;
  for(var i = 0; i < input.length; i++)
  {
    x = input.charCodeAt(i);
    output += hex_tab.charAt((x >>> 4) & 0x0F)
           +  hex_tab.charAt( x        & 0x0F);
  }
  return output;
}

/*
 * Convert a raw string to a base-64 string
 */
function rstr2b64(input)
{
  try { b64pad } catch(e) { b64pad=''; }
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var output = "";
  var len = input.length;
  for(var i = 0; i < len; i += 3)
  {
    var triplet = (input.charCodeAt(i) << 16)
                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
    }
  }
  return output;
}

/*
 * Convert a raw string to an arbitrary string encoding
 */
function rstr2any(input, encoding)
{
  var divisor = encoding.length;
  var i, j, q, x, quotient;

  /* Convert to an array of 16-bit big-endian values, forming the dividend */
  var dividend = Array(Math.ceil(input.length / 2));
  for(i = 0; i < dividend.length; i++)
  {
    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
  }

  /*
   * Repeatedly perform a long division. The binary array forms the dividend,
   * the length of the encoding is the divisor. Once computed, the quotient
   * forms the dividend for the next step. All remainders are stored for later
   * use.
   */
  var full_length = Math.ceil(input.length * 8 /
                                    (Math.log(encoding.length) / Math.log(2)));
  var remainders = Array(full_length);
  for(j = 0; j < full_length; j++)
  {
    quotient = Array();
    x = 0;
    for(i = 0; i < dividend.length; i++)
    {
      x = (x << 16) + dividend[i];
      q = Math.floor(x / divisor);
      x -= q * divisor;
      if(quotient.length > 0 || q > 0)
        quotient[quotient.length] = q;
    }
    remainders[j] = x;
    dividend = quotient;
  }

  /* Convert the remainders to the output string */
  var output = "";
  for(i = remainders.length - 1; i >= 0; i--)
    output += encoding.charAt(remainders[i]);

  return output;
}

/*
 * Encode a string as utf-8.
 * For efficiency, this assumes the input is valid utf-16.
 */
function str2rstr_utf8(input)
{
  var output = "";
  var i = -1;
  var x, y;

  while(++i < input.length)
  {
    /* Decode utf-16 surrogate pairs */
    x = input.charCodeAt(i);
    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
    {
      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
      i++;
    }

    /* Encode output as utf-8 */
    if(x <= 0x7F)
      output += String.fromCharCode(x);
    else if(x <= 0x7FF)
      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0xFFFF)
      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
    else if(x <= 0x1FFFFF)
      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
                                    0x80 | ((x >>> 12) & 0x3F),
                                    0x80 | ((x >>> 6 ) & 0x3F),
                                    0x80 | ( x         & 0x3F));
  }
  return output;
}

/*
 * Encode a string as utf-16
 */
function str2rstr_utf16le(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
                                  (input.charCodeAt(i) >>> 8) & 0xFF);
  return output;
}

function str2rstr_utf16be(input)
{
  var output = "";
  for(var i = 0; i < input.length; i++)
    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
                                   input.charCodeAt(i)        & 0xFF);
  return output;
}

/*
 * Convert a raw string to an array of little-endian words
 * Characters >255 have their high-byte silently ignored.
 */
function rstr2binl(input)
{
  var output = Array(input.length >> 2);
  for(var i = 0; i < output.length; i++)
    output[i] = 0;
  for(var i = 0; i < input.length * 8; i += 8)
    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (i%32);
  return output;
}

/*
 * Convert an array of little-endian words to a string
 */
function binl2rstr(input)
{
  var output = "";
  for(var i = 0; i < input.length * 32; i += 8)
    output += String.fromCharCode((input[i>>5] >>> (i % 32)) & 0xFF);
  return output;
}

/*
 * Calculate the MD5 of an array of little-endian words, and a bit length.
 */
function binl_md5(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << ((len) % 32);
  x[(((len + 64) >>> 9) << 4) + 14] = len;

  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;

    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);

    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);

    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);

    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
  }
  return Array(a, b, c, d);
}

/*
 * These functions implement the four basic operations the algorithm uses.
 */
function md5_cmn(q, a, b, x, s, t)
{
  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
}
function md5_ff(a, b, c, d, x, s, t)
{
  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
}
function md5_gg(a, b, c, d, x, s, t)
{
  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
}
function md5_hh(a, b, c, d, x, s, t)
{
  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
}
function md5_ii(a, b, c, d, x, s, t)
{
  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function bit_rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-11, 19:26
autor: mina86
Czyli w bazie danych hasło przechowujesz jako czysty tekst, co jest najgłupszą rzeczą jaką możesz zrobić.

UPDATE: No dobra, jako MD5 rzeczywistego hasła, ale to tylko mała różnica, bo znając to MD5 można się zalogować, zatem efektywnie właśnie ten skrót jest hasłem, a to co użytkownik wpisuje dziwną jego reprezentacją. Dzięki temu „zabezpieczasz” użytkownika przed sytuacją, gdy baza z hasłami zostanie wykradziona i ktoś będzie próbował użyć haseł na innych stronach, ale z racji, że używane jest czyste MD5 nie jest to wcale duże zabezpieczenie, bo łamanie MD5 to drobnostka. W bazie danych, hasła powinny być hashowane co najmniej za pomocą SHA1 i obowiązkowo z solą.

Chellange-response używa się z połączeniem z podpisem cyfrowym. Serwer posiada klucz publiczny, wysyła klientowi jakiś losowy ciąg, klient odsyła podpis tego ciągu, serwer weryfikuje podpis. Samo MD5 tutaj nie wystarcza i nic nie daje, a wręcz może tylko pogorszyć sytuację.

Jeżeli boisz się ataków man-in-the-middle (bo tylko przed tym to co pokazałeś broni), przeprowadzaj logowanie poprzez HTTPS.

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-11, 20:23
autor: webster
mina86, Spoko, loozuj, Zadyszki Dostaniesz :D Ja nie mówię, że to gdzieś wdrażam produkcyjnie :) Zobaczyłem post faceta, zainteresowałem się, pobawiłem :) tyle :)

Poza tym, hasło w bazie nie jest przetrzymywane zwykłym tekstem. Dodatkowo, jeśli policy wymaga hasło z kombinacją cyfr w środku ciągu znaków, to odgadnięcie na zwykłym sprzęcie nie wchodzi w grę - chyba że się mylę, ale bierzmy to stulecie pod uwagę :)

Kolejna sprawa, tak, chodzi o man-in-the-middle. Https tą metodą też można ugryźć, ludzie i tak klikają "mimo wszystko" nawet gdy certyfikat jest nie potwierdzony. Podsumowując, żadne to zabezpieczenie, jendak przesyłany ciąg MD5 z każdą nową sesją jest inny i osobie która go wykradnie na nic się przyda.

Przy metodzie man-in-the-middle, zawsze można się podszyć pod daną stronę, i nic nie uratuje przed kradzieżą jak user to dalej zwykły user.

Mimo wszystko dzięki za rozprawkę :)

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-11, 20:55
autor: mina86
webster pisze:Dodatkowo, jeśli policy wymaga hasło z kombinacją cyfr w środku ciągu znaków, to odgadnięcie na zwykłym sprzęcie nie wchodzi w grę - chyba że się mylę, ale bierzmy to stulecie pod uwagę
Z użyciem tablic tęczowych bierzemy pod uwagę minuty. Dobrym zabezpieczeniem przed tego typu atakiem jest sól.
webster pisze:Kolejna sprawa, tak, chodzi o man-in-the-middle.
No to super, bo to co opisujesz przed man-in-the-middle nie chroni.

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-11, 21:02
autor: webster
No jak nie chroni, kiedy przed wysłaniem wartości z form, wartość jest przekazywana do js. Wartość w submicie jest już zachaszowana w md5 z kluczem. W man.in. podejrzysz wartość która wysłana została z formularza, a tym będzie za każdym razem inny hash.

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-11, 23:09
autor: mina86
A po co mi znajomość hasła, skoro właśnie dostałem hash, którym mogę się zalogować (bo przecież komunikacja idzie przeze mnie) i zrobić co chcę?

Jedyne co ten kod robi, to trochę utrudnia wyciągnięcie hasła, ale tak jak napisałem, z tablicami tęczowymi odtworzenie hasła nie powinno być dużym problemem.

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-11, 23:16
autor: webster
buehue :D mina86 :D no jak się zalogować - hash Dostałeś, ale tylko dla tej jednej jedynej sesji :) Dostałeś "jednorazowy" hash, Rozumiesz? to, że go Znasz nic Tobie nie daje. Najlepiej jak Weźmiesz sobie to co podałem w code i Potestujesz, Zobaczysz o co chodzi.

Pozdr.

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-12, 03:13
autor: mina86
Ale w trakcie tej sesji mogę robić co chcę.

PS. Oczywyście zakładam, że w prawdziwej implementacji, zamierzasz przechowywać klucz po stronie serwera, bo jeżeli to ma działąć dokładnie tak jak w kodzie powyżej, to przed niczym nie chroni.

Re: [Rozw.] PHP mysql + challenge key method

: 2013-01-12, 14:11
autor: ydoom
No nie wiem Webster... Ta funkcja JS "szyfruj" - albo ją wywołujesz przed wysłaniem formularz albo nie. Jeśli nie wywołujesz to hasło i tak jest przesyłane, więc reszta jest tylko dekoracją, która nic nie wnosi. A jeśli "szyfruj" jest wywoływane, to wyciek bazy danych pozwala obliczyć prawidłową odpowiedź dla każdego "challenge", obojętnie czy trzymasz hasła jawnie czy ich hasze.

Tak na boku, to dobrze jeszcze byłoby po stronie serwera pamiętać jaki "challenge" został wysłany do danego użytkownika, tak żeby "złośliwy użytkownik" nie mógł narzucić swojego :)