Talk:HTTP Post request to a PHP server

From Second Life Wiki
Jump to navigation Jump to search


Is it possible to provide known input values and intercept the hash as it is passed to the web server thereby determining the secret key? Is providing the hash as part of the URL the best strategy? User:Hackshaven Harford

Transmitting the hash is perfectly safe however the method for calculating the hash will lead to the eventual brute forcing of SECRET_NUMBER. Since this script is open source and only uses a 32bit secret, an attack only needs to intercept one message. At the point in the PHP script where the hash is checked the attacker would systematically try every single MD5 nonce, until one of them worked. A year or so ago I was doing some consulting for a guy who was developing a similar system. Using an open source MD5 implementation written in C that I tweaked, I was able to try every nonce in less then 24 hours on a 600MHz Pentium 3. Imagine how fast it would run if it were multi-threaded on a modern multi-core system, now imagine a small cluster of modern computers. If you don't mind it taking several months you can also do this in LSL. If you want to make sure you don't get communication injections don't use this script. -- Strife Onizuka 09:17, 25 April 2007 (PDT)

Strife makes a great point, 32bits is not enough. Strife, I'd like to know what you think is the correct way given the tools we've got. In the meantime, I think you can afford a significant amount of extra protection by using a longer passphrase in place of the 32bit nonce.

string XREQUEST_PASSPHRASE = "nVBh)cCBy7l6ieihewF3Y__QZZ";
string hash=llMD5String(body + XREQUEST_PASSPHRASE,0);

If you make some guesstimates about how long (z) it takes to brute force (x) bits on a cluster of cost (y), you can then measure your paranoia precisely and create a passphrase to suit. Any objections to this implementation? It's still not great, but what else have we got? --Nite Zelmanov 20:37, 27 September 2007 (PDT)

For general paranoia purposes you should use the nonce and a prefixed string; waste not, want not. Regardless how you affix the secret, it is my belief that the secret shouldn't be a string or an integer but an algorithm. A black box that can be fed a secret & the day/time and spit out a reproducible response. You then use the result of that alg as your secret for your hash. When someone cracks the secret used to hash a single message it won't be the same secret used on other messages, or at least not many; hopefully by the time they have cracked the temporal secret it will have expired. An attacker would have to brute force multiple temporal secrets to even start trying to figure out how the black box worked. By combining time with your secret in a non-obvious way you can protect your secret from direct brute forcing. There are other ways... If you know every message is going to be received, you can increment your secret (as a result of passing it through the black box). You want to prefix your (temporary) secret and not postfix it to the data you are hashing. MD5 uses 64 byte blocks, you build the final hash one block at a time. If the secret is postfixed the cracker can cache the temporary value up to the last complete block; in effect they only have to brute force the hash on one block instead of the complete message. By prefixing the secret the caching optimization can't be used. -- Strife Onizuka 22:03, 27 September 2007 (PDT)

A simple black box:

//I prefer an MD5 because it shakes up the bits.
string BlackBox(string secret, integer nonce)//changes every minute
{
    return llMD5String(llGetSubString(llGetTimestamp(),0,15) + secret, nonce);
}

string Hash(string data, string secret, integer nonce)
{
    return llMD5String(llMD5String(llGetSubString(llGetTimestamp(),0,15) + secret, nonce ^ 0x7e9b23f1) + data, nonce);
}

string Hash2(string data, string secret, integer nonce, integer offset)
{//for the main hash, the nonce is a moving target.
//In essence this function allows for the use of 3 secrets: secret, nonce and offset.
    return llMD5String(data, (integer)("0x" + llGetSubString(llMD5String(llGetSubString(llGetTimestamp(),0,15) + secret, nonce),offset,(offset + 7)%32)));
}

Strife Onizuka

dual Key PGP?

Could this be written to use dual key (public/private) encryption? Or would that require a full implementation of PGP for LSL?

For example: an lsl wrapper could be written/added for GnuPG, assuming it is installed on Linden's servers. Then the object could be given the public key of the web resource it is accessing, and only the web server (also using GnuPG) could decrypt using its private key.

An encryted return could be achieved by having every member UID have a public and private key (public key published as part of a Linden web service, private key only known to the server -- not even the user would know it), and the web server would encrypt the message using the user's public key, and the object would access another wrapper function using the user's UID, the function accessing the user's private key and returning the decrypted message.

Any thoughts? -- Caelgarr Oconnell

PGP Brute force

This library did offer 2 things:

(1) HTTP Post
(2) MD5 CRC

1 - For the HTTP Post to work, you have to format the posted data in a certain way. With this technique you can send a List of variable to a server. And the server can collect the data using its normal Post variables. The result s technique

        if (i>0) body+="&";
        body+=llEscapeURL(varname)+"="+llEscapeURL(varvalue);

2 - The MD5 Crc, controls that the request is correct. This makes a checksum of the entire request and make sure that it was received properly. As you said, this not the most secure algorithm. The checksum number is 32 bits. So if you work brute force on it (assuming you know the host name, the URL, and the variables names), every request is stopping the script for 2 seconds. It would take 70 years on average to find the right checksum. You could obviously run simultaneous requests to an extent but this will still take a substantial amount of time to crack.

However the security is not really all in the checksum. As far as I know the request is not done on the client machine. The web request is done from Linden server to your own or hosted website. In order to crack the request you would need to guess the Host name, the URL of the HTTP post, and the magic number.

This said, I take your point about the fact that you could still crack it. So, I will add a SECURE_STRING parameter to make the SECURE_NUMBER harder to guess when the posted variable is empty. I think with this amendment it then become secure enough for most users.

-- Corto Maltese

I found an error in your php code:


$calcHash=md5($body.$SECRET_STRING.':'.$SECRET_NUMBER);


must be


$calcHash=md5($body.llEscapeURL($SECRET_STRING).':'.$SECRET_NUMBER);


or it will return a different hash.