Web API Security

2019-01-17 05:13发布

问题:

I'm asked to write a Web API for an application (pc executable, not web-app) that will allow sending emails.
A user clicks something, the app communicates with the API which generates an email and sends it out.

I have to make sure noone unauthorised will have access to the API, so I need to make some kind of authentication and I haven't got an idea how to do it correctly.

There will be more applications accessing the API.

First thought was - send username and password, but this doesn't solve the problem really. Because if someone decompiles the application, they'll have the request url and variables including user/password or simply it can just be sniffed.

so... what options do I have?

I'm fairly sure secure connection (SSL) is not available to me at the moment, but still, this won't help me against the decompiling problem, will it?


EDIT

I haven't said that initially, but the user will not be asked for the username/password. It's the application(s) that will have to be authenticated, not users of the application(s).

回答1:

I would recommend you check out OAuth. it should definitely help you out in sorting out the security issues with authorizing tools to access your API.

http://oauth.net



回答2:

The distribution of your software is really the crux of the problem. Hashing user names and passwords and storing them in the software isn't any more useful than storing un-hashed values, as either one would work to access the API server. If you're going to implement usernames and passwords for your users, I think you can use that as a pre-cursor to API control without storing the values in the software itself. Let me describe this in two parts.

Request Signatures

The most common method in use for API request verification is request signatures. Basically, before a request is sent to an API server, the parameters in the request are sorted, and a unique key is added to the mix. The whole lot is then used to produce a hash, which is appended to the request. For example:

public static function generateRequestString(array $params, $secretKey)
{
    $params['signature'] = self::generateSignature($params, $secretKey);
    return http_build_query($params,'','&');
}

public static function generateSignature($secretKey, array $params)
{
    $reqString = $secretKey;
    ksort($params);
    foreach($params as $k => $v)
    {
        $reqString .= $k . $v;
    }
    return md5($reqString);
}

You could create an API request query string using the above code simply by calling the generateRequestString() method with an array of all the parameters you wanted to send. The secret key is something that is provided uniquely to each user of the API. Generally you pass in your user id to the API server along with the signature, and the API server uses your id to fetch your secret key from the local database and verify the request in the same way that you built it. Assuming that the key and user id are correct, that user should be the only one able to generate the correct signature. Note that the key is never passed in the API request.

Unfortunately, this requires every user to have a unique key, which is a problem for your desktop app. Which leads me to step two.

Temporal Keys

So you can't distribute keys with the application because it can be decompiled, and the keys would get out. To counter-act that, you could make very short-lived keys.

Assuming that you've implemented a part of the desktop app that asks users for their username and password, you can have the application perform an authentication request to your server. On a successful authentication, you could return a temporal key with the response, which the desktop app could then store for the lifetime of the authorized session, and use for API requests. Because you mentioned that you can't use SSL, this initial authentication is the most vulnerable part, and you have to live with some limitations.

The article Andy E suggested is a good approach (I voted it up). It's basically a handshake to establish a short-lived key that can be used to authenticate. The same key could be used for signature hashing. You could also take your chances and just send the username/password unencrypted and get a temporal key (it would only happen once), but you'd have to be aware that it could be sniffed.

Summary

If you can establish a temporal session key, you won't have to store anything in the client program that can be decompiled. A username/password sent once to your server should be enough to establish that. Once you have that key, you can use it to create requests in the desktop apps, and verify requests on the API server.



回答3:

Someone is always going to be able to decompile and hunt for the variables. An obfuscator might be able to hide them a little better. Sniffing is also easy without SSL unless you use a private and public keyset to encrypt the request data client side and decrypt server side (but obviously this key will be stored in the client application).

The best thing to do is provide as many layers of protection as you think you will need, creating a secure connection and obfuscating your code. You could look at the following article, which demonstrates a secure connection without using SSL:

http://www.codeproject.com/KB/security/SecureStream.aspx

As mattjames mentioned, you should never store passwords in plain text format. When the user enters their password into the application, store a hash of the password. The same hash should be stored on the server. That way, if the hash is seen by an interceptor they at least wouldn't see the user's original password.



回答4:

You will need to use SSL if you need to prevent people from seeing the plain text password that is sent from the app over the network to the API.

For the decompilation issue, you would want to store the hash of the password in the API, not the original password. See explanation here: http://phpsec.org/articles/2005/password-hashing.html.