Skip to content

Instantly share code, notes, and snippets.

@getify
Created July 4, 2010 01:54
Show Gist options
  • Select an option

  • Save getify/463013 to your computer and use it in GitHub Desktop.

Select an option

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");
@getify
Copy link
Copy Markdown
Author

getify commented Jul 7, 2010

@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.

@getify
Copy link
Copy Markdown
Author

getify commented Jul 7, 2010

"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.

@oyvindkinsey
Copy link
Copy Markdown

@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...

@getify
Copy link
Copy Markdown
Author

getify commented Jul 7, 2010

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".

@fearphage
Copy link
Copy Markdown

@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.

@oyvindkinsey
Copy link
Copy Markdown

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

@getify
Copy link
Copy Markdown
Author

getify commented Jul 7, 2010

@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.

@getify
Copy link
Copy Markdown
Author

getify commented Jul 7, 2010

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.

@Pointy
Copy link
Copy Markdown

Pointy commented Jul 7, 2010

Things that Rhino needs some help with in order to decode a "noalnum" string:

  1. global object needs to be referenced by a variable named "window" (duhh)
  2. need "atob" and "btoa" functions
  3. the "toString" function on the global/window object needs to return "[object Window]" instead of "[object Global]"
  4. 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}")
  5. The String prototype needs a working "fontcolor" function (trivial)
  6. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment