This looks good, as far as I can tell. Good luck with implementation.
Evan
On Mar 28, 2006, at 2:51 AM, Satyam wrote:
You are absolutely right! I love this list!
I didn't realize that I was sending the session_id. Let's see if
this works.
On first serving the login page I create a session and also a
unique_id via uniqid(). I store the unique_id in the session and
send it as a challenge to the client along the login form. This
unique_id is the nonce. Upon receiving the login data and checking
it for good, I delete the unique_id from the session. If I receive
a made up session_id, the password data won't match the unique_id
stored in the session (probably there will be none). The nonce
stored in the session will be a new one, even for the same session,
each time the login screen is requested. If the login fails, upon
retry a new unique_id will be generated and sent for the retry.
Thus, session_ids and unique_ids combinations (almost) never get
repeated. Thus, is you knew the answer to one, it won't help you
with any other because even if you can make up a session_id, you
cannot change the unique_id the server made and, since unique_ids
don't repeat, there is no chance that you have ever sniffed the
password hashed with that unique_id.
Satyam
----- Original Message ----- From: "Evan Priestley" <spam@xxxxxxxx>
To: "Satyam" <Satyam@xxxxxxxxxxxxx>
Cc: <php-general@xxxxxxxxxxxxx>
Sent: Monday, March 27, 2006 11:58 PM
Subject: Re: protecting passwords when SSL is not available
The client cannot and does not send the session_id() used to
hash the password back to the server, it does not need to, the
client got it from the server.
It does send the session ID back though, because that's how the
user maintains their session across requests.
For simplicity, let the totally made up word "noncehash" represent
hash(hash(password)+nonce) -- that is, the hash of 'the nonce
appended to the hash of the password'.
Generally, if the client ONLY sends (a) a user name and (b) a
noncehash, then the server has no way to tell which nonce was
originally issued. Therefor, the client has to send (a) a user
name, (b) a noncehash, and (c) some key which can identify which
nonce was sent. This key might be the session ID, the nonce
itself, or something else[1].
If the key is the nonce itself and the only server-side validation
is that the response noncehash is correct for the supplied nonce,
attacker Bob can observe ANY valid nonce/noncehash combination
Alice submits and replay it to gain access[2].
If the key is the session ID, and the only server-side validation
is still that the response noncehash is correct for the given
session ID, attacker Bob can STILL observe ANY valid session ID /
noncehash combination Alice submits (she _is_ submitting the
session ID -- the nonce, here -- because every request always
includes the session ID when sessions are being used) and replay
it immediately to gain access. This is "session hijacking", and
it works because PHP will let Bob into Alice's session as long as
he knows her session ID[3].
Instead, suppose the server-side validation is a little stronger:
it checks that the response noncehash is correct for the given
session ID, but ALSO checks to make sure that this session ID
hasn't logged in yet. Now Bob can't replay the response
immediately. He can still just hijack the logged-in session,
though, and he might be able to replay the attack after Alice's
session has expired, because the flag that says "this session has
already logged in" will also have expired. I'm not sure if PHP
will create an expired session ID for you; presumably it won't,
but if you're writing your own session handler or implementing
nonced password transmission in some other programming language,
this might be a viable attack vector.
Evan
[1] It could even be the username, if the login process went like
this: client sends server username, server generates a nonce and
stores it in the user table, server sends generated nonce to
client, client sends hash(hash(password)+nonce) to server -- but
then an attacker can perform a DOS attack by repeatedly sending
the server a username so that it regenerates nonces more quickly
than the real user can log in. In any case, (a), (b) and (c) do
not necessarily need to be three separate pieces of information,
since one piece of information can serve multiple roles.
[2] Unless nonces are stored in a database and flagged as "used"
afterward. You can also, e.g., generate nonces in the form
"timestamp,hash(timestamp+secret)"; google for more on this.
[3] Unless you're e.g. restricting sessions by IP, but this is
potentially a whole different can of worms.
It is the server that challenges the client with a session_id()
(or any other random) sent clear and the client has to take the
challenge and combine it with the password (which is not
transmitted clear). Thus the client cannot tell the server,
'this is user xxx, with password yyyy hashed under session_id
zzzz'. It is the server that challenges the client with the
session_id. The attacker might collect enough samples so if a
challenge repeats, he can have the answer ready, but that is
unlikely with long enough challenges (and session_ids are long).
As for sending the session_id() from the server to the client in
clear (not encrypted) it seems to me it doesn't make any sense
to alter it in any way since, after all, you are also sending
the algorithm in Javascript to the client, which is clear for
anyone to see, so there would be no point in trying to hide the
session_id in any way, and I don't think it would help the
overall security.
My bank does use SSL, of course, and it still requires
confirmation to do critical processes so, that might be a
partial solution to spoofing.
Anyway, this is a poor man replacement for SSL, with
limitations, but it is good to know what are those limitations.
Thanks for your help
Satyam
----- Original Message ----- From: "Evan Priestley" <spam@xxxxxxxx>
To: "Satyam" <Satyam@xxxxxxxxxxxxx>
Cc: <php-general@xxxxxxxxxxxxx>
Sent: Monday, March 27, 2006 5:41 PM
Subject: Re: protecting passwords when SSL is not available
This is called a "nonce"[1], and the method you've described
will give you marginally less awful security than submitting
a plaintext password or an unadulterated hash of the password,
but, obviously, is in no way a substitute for real SSL. For
instance, if this password puts the session in a "logged in"
state, an attacker with the capacity to sniff the password can
also sniff the "logged in" session ID after authentication.
You can potentially bind the login to IP, but will prevent
users behind rotating proxies from using your service and may
not protect users behind non-rotating proxies, and the source
IP for a request can be spoofed. Alternatively, you can require
the password for any action requiring authorization (and never
put the user in a "logged in" state), but this will impose
substantial constraints on your design. And, of course, an
attacker can still observe any other data you transmit.
If you implement nonced password transmission, absolutely
ensure that an attacker can not alter the provided nonce. For
instance, if Bob sees Alice log in under nonce "abc123" (her
PHP session ID), what happens if Bob later executes a replay
attack by mimicking her form submission (username: alice,
password_hash: def456, session_id: abc123)? If he can gain
access via replay attack at any time after Alice's first login
([a] while her session is valid or [b] after it has expired
presumably being the critical periods), the system offers no
security over non-nonced password transmission.
Evan
[1] http://en.wikipedia.org/wiki/Nonce
On Mar 27, 2006, at 8:30 AM, Satyam wrote:
I know the answer to a secure site is SSL, but what if you are
on a shared host, SSL is unavailable and you still want some
sort of security?
This is what I came by and I would appreciate any advice as to
possible security holes in it. There is a big hole I know,
which is the screen to change the password, I find no way to
secure that one. But lets go to what I do have.
I found at http://pajhome.org.uk/crypt/md5/md5src.html a
Javascript version of the MD5 algorithm.
I checked it against the PHP md5() function:
<html><head><title>MD5 test</title>
<script language="JavaScript" src="includes/md5.js"></script>
<script>
function enOnLoad() {
document.getElementById('prueba').innerHTML = md5_vm_test
(); // test provided by the library
<?php
$valor = rand();
?>
document.getElementById('p2').innerHTML = hex_md5('<?=$valor?>');
}
</script>
</head><body onLoad="enOnLoad();">
<div id=prueba></div>
<div id=p2></div>
<div><?=md5($valor)?></div>
</body></html>
And the results of the Javascript and the PHP md5() functions
are the same (the JS source has a couple of parameters to
play with, but the defaults proved good enough)
So, my idea is that in the login script, PHP will send a random
number along with the login form. That random might actually
be the session_id() but if not, the random value sent has to
be stored in a session variable. (I really don't see any
reason not to use the session_id()).
On clicking on submit to send the login form, the password
field would be replaced by the result of
a) calculate the MD5() of the password, trimmed of
whitespace. This should be the same value stored in the user
table of the database.
b) concatenate this value with the random number (or session_id
()) provided by the server.
c) calculate the MD5() of this
d) replace it into the original password field and let the
submit proceed.
On the server side, when the login data is received:
a) retrieve the password field from the user table on the
database. This should actually be the MD5-encripted of the
actual password.
b) concatenate this value with the session_id() or whatever
random you generated before
c) calculate the MD5() of this
d) compare with received value. If they match, they come from
the same password.
Would it work?
Satyam
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php
--
PHP General Mailing List (http://www.php.net/)
To unsubscribe, visit: http://www.php.net/unsub.php