views:

45

answers:

4

I'm writing a JavaScript function that needs to uphold three properties:

  • be very small and lightweight - no external libraries
  • encode a string in such a way as to be able to be passed as a GET parameter
  • this string must be decoded again at its destination

Effectively, it authenticates the user by sending his username and password to a PHP page which then verifies it. This is done via GET because I haven't yet found a way of doing a background cross-domain POST request. The trouble is that if the user has a character such as '#' or similar in his password, it doesn't get sent properly.

Currently to avoid this, I encode() the password string before sending it, which allows it to be received without problems. However, I read that PHP's urldecode() is not a perfect analog for this, as there are corner cases which are treated differently (i.e. ' ', '+', etc). Sadly I cannot find this document anymore, so I cannot quote it, but the gist was that one of them converts spaces into '+' signs, which the other treats as an actual plus sign, or something like that...

As such, I'm looking for a Javascript function that can take a string and make it URL-safe, and which has a perfect reversal function in PHP so that the original string can be recovered.

The arguably awful code I currently use to achieve this:

login.onsubmit = function(){
    loginFailMsg.style.display = 'none';
    var inputs = login.getElementsByTagName('input');
    var formdata = 
        'username='+inputs[0].value+'&password='+encode(inputs[1].value);
    submit.src = formtarget+'/auth/bklt?'+formdata;
    userinfo = undefined;
    setTimeout(getUserinfo,300);
    return false;
};
+1  A: 

I don't think there's such a thing as a reversible hash function. There are plenty of javascript md5 libraries available, however.

Karl Bielefeldt
apologies, I misspoke - in absence of md5 i'm looking for some kind of translation, i.e. base64 or something... basically something that'll make sure it gets passed to the script correctly
Mala
A: 

Why not do what you suggested in your comment to Karl and use base64 encoding?

Nifle
the script needs to be as absolutely short as possible, so I'm avoiding pulling in any libraries. I was just wondering if there were anything like base64 encoding or similar built into javascript
Mala
@Mala - Mozilla and webkit browsers support it natively apparently. See this answer on SO http://stackoverflow.com/questions/246801/how-can-you-encode-to-base64-using-javascript/247261#247261
Nifle
+1  A: 
'&password='+encode(inputs[1].value)

Where's encode function coming from? Seems to me the quick answer to your question is using encodeURIComponent() instead, available since JavaScript 1.5. See also Comparing escape(), encodeURI(), and encodeURIComponent(); it does not encode everything either, but does encode all the server expects it to.

(As for cross-domain AJAX POST calls, I'd really have a look at "JSON with Padding". See JSONP with jQuery that I mentioned in the comments earlier. This will also prevent issues with the timeout you've randomly chosen, and jQuery will also help you, a lot, to get rid of inputs[0].value and the like. And, as you apparently already have a MD5 hash on the server, I'd really hash the password client side as well --see Karl's answer-- and compare those hashes instead. Respect your user's password and your own time, drop that no external libraries requirement!)

Arjan
using escape, the # isn't a problem... it's just php's urldecode does not produce the exact input. There are a few corner cases (I wish I could remember where I read this) involving ' ', '+', and a few other characters
Mala
@Mala, but *why* use `escape`? My point is: not needed!
Arjan
Oh, as you're not using `<form>`, I guess? Modern browsers also support `encodeURI` and the like, but I guess indeed `escape` should give you the same result as a plain `<form>`.
Arjan
Mala
The trouble with jQuery is that it's an external library, and is quite large (compared to nothing, that is). I'm trying to keep the entire script under 25kb. Which makes including jQuery's 72kb minified version out of the question. However, encodeURIComponent() appears (from the doc and from testing) to accomplish my needs.
Mala
I'm also rewriting it to avoid that hardcoded timeout, because it's obviously a problem. Rather it'll wait for a little while and checking again, with a loop counter so that if that happens too often it sends the request again. Sometimes you just gotta get your hands dirty...
Mala
@Mala, just as an aside: when [using Google's CDN for jQuery](http://encosia.com/2008/12/10/3-reasons-why-you-should-let-google-host-jquery-for-you/) then many of your users might already have jQuery in their cache anyhow, just by visiting other sites that use the same thing from the same Google URL. And though not on Google's CDN: there's [a MD5 plugin](http://plugins.jquery.com/project/md5) too, making your users happy even though they won't even know it. ;-)
Arjan
@Arjan thanks for the links. In this particular instance though, since it's a bookmarklet, the prospect of adding a <script> with the appropriate url and repeatedly setTimeout'ing until I can resolve something inside the script just seems like overkill when a "good enough" solution like encodeURIComponent presents itself
Mala
Aha, @Mala, it's a bookmarklet! Just as an aside again: bookmarklets *can* download external libraries. See [How do you use http://toread.cc without a bookmarklet?](http://superuser.com/questions/64475/how-do-you-use-http-toread-cc-without-a-bookmarklet/64494#64494), or see [The Printliminator](http://css-tricks.com/examples/ThePrintliminator/) that even loads jQuery on the fly.
Arjan
+2  A: 

encodeURIComponent, PHP will decode it automatically when populating $_POST or $_GET

David Dorward