Message Boards Message Boards

13
|
22642 Views
|
6 Replies
|
26 Total Likes
View groups...
Share
Share this post:

Single-Origin Authentication for Cloud APIs

Posted 11 years ago

While watching the Wolfram Language in the Cloud presentation, the subject of authentication came up. While the Programming Cloud supposedly does support OAuth for APIs, that seems a little over-the-top if you're just accessing it from one app. While application auth tokens are coming in the future, I developed a simple solution and thought I'd share it.

I have this incredibly useful and worthwhile Instant API:

api = APIFunction[{"text" -> "String", "auth" -> "String"},
   If[#auth == "MySuperSecretAndSecureAccessKey", 
     Return["You said: " <> #text], Return["Access denied!"]] &];

If I go to https://www.wolframcloud.com/objects/43fcc0c4-d4d1-4bc8-952b-e33360dc830b?text=Hi!&auth=MySuperSecretAndSecureAccessKey my text is echoed back to me. And if I use an incorrect key, it refuses to cooperate.

Foolproof, right?

Wrong.

If I eliminate the arguments from the URL, I get this error page: API Web Error Report

And inside the JSON object-

Traceback

-this traceback. Which is nice, except for the fact that it has my key.

So how do we fix this?

Many websites require username-password authentication. It is standard practice not to store the password in plaintext, but to calculate a hash and check logins against it.

I can get the hash of a string in the Wolfram Language with Hash[]:

In[1]:= Hash["MySuperSecretAndSecureAccessKey"]
Out[1]= 5806601739209730667

I can modify my function to use that hash, like so:

api = APIFunction[{"text" -> "String", "auth" -> "String"},
   If[Hash[#auth] == 5806601739209730667, 
     Return["You said: " <> #text], Return["Access denied!"]] &];

The API still works, and no modifications to the parameters are required. If I invoke the name error, all the recipient gets is my hash, which is almost completely useless.

Error report with hash authentication

Enjoy your newly secured Cloud API!

POSTED BY: Jesse Friedman
6 Replies

Interesting, but now I believe that PermissionsKey is the best way to do that.

obj= CloudDeploy[APIFunction[{"n"->Integer},#n^2&],Permissions->{PermissionsKey["thekey"]->"Execute"}]
URLExecute[obj, {"n" -> 123, "_key" -> "thekey"}]

Tutorial: https://reference.wolfram.com/language/workflow/DeployAnAPIThatUsesAPermissionsKey.html

POSTED BY: Rodrigo Murta

Cryptographic hash functions are designed to be irreversible, or at least extremely hard to reverse. For all intents and purposes, with a good hash algorithm it is virtually impossible to calculate an input that hashes to a desired output. However, some hash functions such as MD5 have been compromised, and are now considered insecure, due to the relative simplicity of finding hash "collisions", multiple inputs that hash to the same output.

POSTED BY: Jesse Friedman

But would not that expose the secret key in the hash?

POSTED BY: Hristo Vrigazov

Very interesting stuff here. I bet you could create some simple formulas for dynamic salt keys that would be too hard to guess. Maybe something like today's date squared minus yesterday's date. As long as both sides know the simple dynamic salt key generator formula, it would good enough to stop all but NSA abuse. lol

POSTED BY: David Johnston

Hi, I'm Riccardo one of the developers of this function.

This is super cool!

If you want to make it even more bulletproof, you can create and add a salt to your hash.

http://en.wikipedia.org/wiki/Salt_(cryptography)

A salt is basically a private key shared between your machine and the server, it improves security.

Also, I would suggest to do an hash over all your args.

APIFunction[
{"text" -> "String", "number"->"Number", "auth" -> "String"}, 
If[Hash[{"MySuperSecretSaltKey", #text, #number}] == #auth,
   "You said: " <> #text, 
   "Access denied!"] &]

What the client needs to do now is to send to the api an hash of

Hash[{"MySuperSecretSaltKey", #text, #number}]

In this way every request to the api needs a different signature.

The reason why this can be better is because a man in the middle could steal your request data and do the same request again. If you are signing every request in this way, a man in the middle do not have the key to your api, but only the key to the request you have done.

The method I am suggesting is still not perfect, and can be improved, so if you are interested in security you can read a couple of things you may find interesting.

http://en.wikipedia.org/wiki/Man-in-the-middle_attack

http://en.wikipedia.org/wiki/Cross-site_request_forgery

Great job, let me know if you need anything.

Excellent! And one can also choose which Hash method one would like to use modifying the code (and hash value) using one of the hash code types described in http://reference.wolfram.com/language/ref/Hash.html

By the way, there's no need for the Returns: this works fine:

APIFunction[{"text" -> "String", "auth" -> "String"}, 
 If[Hash[#auth] == 5806601739209730667, 
   "You said: " <> #text, 
   "Access denied!"] &]

P.S. Nice profile shot. Who took it? ;-)

POSTED BY: David Reiss
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract