-
-
Save getify/463013 to your computer and use it in GitHub Desktop.
| <?php | |
| // located at: http://another.tld/auth.php | |
| $api_callback = $_REQUEST["callback"]; | |
| if ($_COOKIE["token_1"] == "abcd1234" && $_GET["token_2"] == "efgh5678") { | |
| $msg = "Yes, your API call was successful!"; | |
| } | |
| else { | |
| $msg = "API call not authorized."; | |
| } | |
| ?> | |
| // this is a JSON-P style response from the API | |
| <?=$api_callback?>({"msg": "<?=$msg?>"}); |
| <?php | |
| // located at: http://another.tld/auth.php | |
| $token_1 = "abcd1234"; | |
| $token_2 = "efgh5678"; | |
| $auth_callback = $_REQUEST["callback"]; | |
| setcookie("token_1",$token_1); | |
| ?> | |
| // in JS, document.domain is not settable or spoofable so it's | |
| // reliable to protect a cross-domain JSON-P call | |
| if (document.domain == "something.tld") { | |
| <?=$auth_callback?>({"token_2": "<?=$token_2?>"}); | |
| } |
| // this file is loaded and run on http://something.tld/index.html | |
| function make_jsonp_call(url) { | |
| var script = document.createElement("script"); | |
| script.src = url; | |
| script.type = "text/javascript"; | |
| document.getElementsByTagName("head")[0].appendChild(script); | |
| } | |
| function api_done(resp) { | |
| alert(resp.msg); | |
| } | |
| function get_auth(auth) { | |
| var token_2 = auth.token_2; | |
| // not only do we have token_2 by way of the auth parameter, | |
| // but token_1 is stored in a browser cookie now. together, | |
| // these two tokens will authorize our API call. | |
| make_jsonp_call("http://another.tld/api.php?token_2="+token_2+"&callback=api_done"); | |
| } | |
| make_jsonp_call("http://another.tld/auth.php?key=987654321&callback=get_auth"); | |
Anything that a browser could feasibly have, be it plugins or otherwise, can be implemented server side - anything.
The only way you can make sure the request is made from a browser is by using a communication channel that is bound to the browser, e.g. Cross Document Messaging. That cannot be spoofed from the server and hence the value of document.domain is reliable.
You could just use easyXDM which runs entirely in the browser, and which guarantees for the 'origin' of each call.
I think this might actually be along the lines of what I was thinking:
http://discogscounter.getfreehosting.co.uk/js-noalnum.php
Basically, you can take a string (like an auth token) and transfer it in JavaScript in a way that pretty much only a browser could feasibly understand, because it relies on peculiarities of how various browser/DOM extensions to JavaScript work... for instance:
(![]+[]) ==> "false" (the string)
[!+[]+!+[]+!+[]] ==> [3]
(![]+[])[!+[]+!+[]+!+[]] ==> "s" (the string/character)
So, by building up the characters of the token using such "encoding" and knowing the browser will automatically decode it, that offers a decent level of protection because of the infeasiblity of a server script reversing that encoding without just implementing not only a JavaScript interpreter, but also the DOM/browser extensions, used for instance to get strings like "alert" to grab characters from, etc.
You realize that it's possible to harness IE or Firefox server-side, right?
@Pointy -- yes, I understand anything is possible, even brute-forcing a 1024bit encryption key in SSL. The point was not to provide something fool proof. The point was to find something that is:
a) still fairly easy for a valid browser to use/interpret without too much extra effort
b) somewhat more difficult for an automated server script to spoof so as to make the effort not worth the payoff...
With my original approach shown above, the tokens were in plain text in the response (both in the cookie header and in the body of the response). This is practically no barrier at all to server automated spoofing. A system like this would be nearly completely transparent to a valid use in a browser, and hopefully a bit more complicated for a server script to automate feasibly.
Theoretically, someone could automate a browser as part of a script. Also, someone could spend the time to generate a table of all the possible patterns of the no-alnum type code and map them back to their valid characters. It just seemed to me like something like this would provide a decent barrier to server spoofing.
@getify, that simply will not work. Anyone who wanted to 'hack' your service could either implement an engine that would comply with your test, or it could utilize the same vm that IE, or any other browser uses to parse and execute the test...
Haven't you heard 'anything that a browser can do, a server can do better' ? :)
No need to script a browser, most javascript engines can be used outside the browser environment, e.g Rhino, JScript and V8.
I was thinking of Jaxer, which is intended to be a complete server-side environment; it's basically a wrapper around Firefox itself. It's not really a hack or anything, other than in its intrinsic essence I suppose. People do use it however.
A thing about security systems is that the value of what you're protecting is determined by the attacker, not you.
@oyvindkinsey -- a decent portion of that noalnum algorithm relies on things that the bare bones javascript engines don't have natively in them, such as special behavior of the window object, presence of things like the alert() function, etc. That's what I mean by saying that the browser would have to be scripted to feasibly unravel these things, or the person would have to create a brute-force dictionary of character mappings....
There's a whole bunch of different ways I could devise to represent the "s" character (the above is only one of many)... so the reverse character mappings for all 65 characters possible in the key seems like it would need to be exhaustively large.
Again, the point is not to find something that's perfect, only something that's a decent barrier better than just plain text sniffing. There's no perfect security mechanism for a house, but just hanging a sticker in your window that you HAVE a security system is more than enough to deter most criminals. It's a risk vs. payoff thing, as with most things in computing science.
"aBFDklwe434r3./sdf424fs"
(![]+[])[+!+[]]+(+[]+(![])[([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(+(+[])+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]][([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][[]]+[])[+!+[]]+(![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(![]+[])[+!+[]][+!+[]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]](![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+[])[!+[]+!+[]][+[]]+(![]+[])[!+[]+!+[]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+[])[+!+[]+[!+[]+!+[]+!+[]]]+(!![]+[])[!+[]+!+[]+!+[]]+[!+[]+!+[]+!+[]+!+[]+[!+[]+!+[]+!+[]+[!+[]+!+[]+!+[]+!+[]]]]+(!![]+[])[+!+[]]+[!+[]+!+[]+!+[]]+[][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]](![]+[])[+!+[]]+(!![]+[])[+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+[])[!+[]+!+[]][+!+[]]+(!![]+([]+[])(![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[+[]]+([][(![]+[])[!+[]+!+[]+!+[]]+(!![]+[][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]])[+!+[]+[+[]]]+(!![]+[])[+!+[]]+(!![]+[])[+[]]]([][(![]+[])[+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]]+(!![]+[])[+[]]+(!![]+[])[!+[]+!+[]+!+[]]+(!![]+[])[+!+[]]]+[])[!+[]+!+[]+!+[]]+(![]+[])[+!+[]]+(![]+[])[!+[]+!+[]]+(![]+[])[!+[]+!+[]]+[])[!+[]+!+[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]+!+[]]+([][[]]+[])[!+[]+!+[]]+(![]+[])[+[]]+[!+[]+!+[]+!+[]+!+[]+[!+[]+!+[]+[!+[]+!+[]+!+[]+!+[]]]]+(![]+[])[+[]]+(![]+[])[!+[]+!+[]+!+[]]
I'd love it if someone could prove to me that reversing that no-alnum to get back the original string shown above is as trivially easy as inspecting plain text. Is it possible? Sure. But I bet it's not as easy as it might sound.
@getify http://ejohn.org/blog/bringing-the-browser-to-the-server/
It's no problem emulating such things as window.alert in e.g Rhino, and Rhino will also execute your noalnum string as plain javascript, resulting in "s" - there's no need for a mapping table.
I'm sorry, but your approach is just not viable as anything other than security by obscurity...
FYI: I just tried that noalnum sequence in a couple of server-JS engines, including the shell for V8. It failed. But it works in the browser just fine. So it's not as easy as "just run it in V8 or Rhino".
@getify you do realize that server size languages are used to generate that stuff, right? Any language could decipher that, it's fairly straight forward. It's a pattern. All you have to do is know what's going on and you can read it just like you're reading this post. This would be child's play to implement on a server. I think you should keep looking for the thing a browser can do that a server cannot.
Of course it won't work in a vanilla vm as it depends (as you your self said) on the presence of objects like window, alert etc.
Here, load this in Rhino before you try again
http://jqueryjs.googlecode.com/svn/trunk/jquery/build/runtest/env.js
@fearphage -- i'm not sure if you realize how noalnum works... but it's not just a single-pass pattern replacement. in that demo URL above, you can "step" through the algorithm one at a time, and see that what it's doing is a bit more complicated than that.
You must know that not all algorithms are trivially reversible. I myself can't prove mathematically that this algorithm is or is not trivial to do so, except that I can assert that I could easily "perturb" this predictable algorithm by randomly choosing any of a thousand different possible replacements for the "s" character... which means that a single API Key string with even a few characters length to it could be randomly converted to a hundred thousand or more different noalnum sequences. The particular demo URL may be predictable/deterministic in that "s" may always be one noalnum sequence, but it'd be trivial to severely complicate the reversing by just adding some randomness in there.
I think it's quite clear (and provable) that the noalnum string would provide a pretty decent barrier to pattern recognition/replacement brute force breaking. Again, it's not perfect and unbreakable, but I suspect it would take a lot of effort at least.
It remains to be proven one way or the other however that it's trivial to load in a server-side DOM emulation like env.js and the noalnums just magically work on the server. I don't have Rhino so I can't verify at the moment. env.js doesn't work in V8, so I couldn't test it there. But I did try just defining some common "window" and "alert" objects/functions, and even the noalnum for the "." character couldn't be evaluated directly.
If someone can prove that "env.js" loaded into Rhino is all it takes to reverse any possibly complicated noalnum, or even most of them, you still haven't proven that this approach has no value. You've simply proved that Rhino+env.js is just one feasible spoofing vector. Anyone who doesn't have that environment (Java for Rhino, right?) still may be dead in the water, or rather, facing an uphill battle at best.
Also, I bet this approach + [something else yet to be devised] could be combined to thwart the Rhino/env.js crowd, or at least get it to the point where it wasn't trivially easy to overcome.
I should also say that while browser (not JS engine) automation, like in a VM, would be something I would like to prevent, the spirit of this question was just to ensure that someone couldn't easily set up a proxy server script against a particular Ajax type request. If it turns out that browser automation in a VM is the only viable way to get around this "security", then we've actually accomplished pretty much what I wanted: make it so that a browser has to interpret an Ajax request.
I'm not concerned with trying to enforce that a browser isn't being automated... there are CAPTCHAs and other such approaches that can be used to ensure a human is sitting at the console.
I liken this to the classic CS problem of trying to take any given matched string and find the exact regular expression that matched it.
Since it can be proven that there are nearly infinitely many different regex's that could match the given string, you can't prove that you can easily find exactly the regex the string came from. The regex match process is a lossy one-way street. There's the easy base regex /thestring/, but what if I had originally randomly started with some other crazy regex that matched the same string? You have no way of knowing which regex I started with, except just starting with the base regex and exhaustively brute-force trying all possible variations on the regex grammar, and asking if that is the regex I started with.
You could probably do this brute force, but it's not gonna be pretty or easy or trivial.
Things that Rhino needs some help with in order to decode a "noalnum" string:
- global object needs to be referenced by a variable named "window" (duhh)
- need "atob" and "btoa" functions
- the "toString" function on the global/window object needs to return "[object Window]" instead of "[object Global]"
- The Array prototype needs a "filter" that doesn't have to do anything in particular other than be a function, and also have (on the function object itself) a toString method that returns the sort of string Firefox returns ("function filter () {\n [native code]\n}")
- The String prototype needs a working "fontcolor" function (trivial)
- The global/window "Date" function has to be replaced by a function that just returns a random Javascript-style date string (this is due to a NullPointerException bug in the Rhine Date() function)
I think that's pretty much it.
upon further thinking it would appear the only way to do this is to somehow transfer the token_2 not in plain text, but in some way encoded so that something unique only to a browser could interpret in a useful way. imagine something like a flash plugin, or something only a browser could feasibly have in it, and using some unique property of that browser feature to decode the encoded token.