I'm building a solution consisting of an app and a server. Server provides some methods (json) and the app uses them. My aim is to make those API methods inaccessible to other clients. What is the best way to do so?
Should I take a look at certificates (to sign every outgoing request)? If yes, where do I start and what is the performance impact of doing so?
What are alternatives?
Put another way, you need a way to distinguish a valid client's request from an invalid client's request. That means the client needs to present credentials that demonstrate the request comes from a valid source.
SSL certificates are an excellent way to assert identity that can be validated. The validity of an SSL certificate can be confirmed if the certificate contains a valid signature created by another certificate known to be secure, a root cert. As noted in other answers an embedded certificate won't do the job because that certificate can be compromised by dissecting the app. Once it is compromised, you can't accept any requests presenting it, locking out all your users.
Instead of one embedded app cert, you need to issue a separate certificate to each valid user. To do that, you need to set up (or outsource to) a Certificate Authority and issue individual, signed certificates to valid clients. Some of these certificate will be compromised by the user -- either because they were hacked, careless or intentionally trying to defraud your service. You'll need to watch for these stolen certificates, place them on a certificate revocation list (CRL) and refuse service to these compromised certificates. Any web server is able to refuse a connection based on a CRL.
This doesn't solve the security issues, it just moves them out of the app. It is still possible for someone to create what appears to be a valid certificate through social engineering or by stealing your root certificate and manufacturing new signed certificates. (These are problems all PKI providers face.)
There will be a performance hit. How much of a hit depends on the number of requests from the app. The iPhone NSURLConnection class provides support for SSL client certificates and client certificates can be installed in the phone from an e-mail or authenticated web request. Managing the infrastructure to support the client certs will require more effort than coding it into the app.
Incidentally, voting down any answer you don't like creates a chilling effect in the community. You're not nearly as likely to get advice -- good or bad -- if you're going to take a whack at everyone's reputation score.
I will now freely admit that it's an interesting question, but I have no idea how it could be done.
Original answer:
Interesting question. Assuming people can't reverse-engineer the iPhone app, the only solution that comes to mind would be to sign requests with a public key, or some other secret known only to the application. By that, I mean adding an extra argument to every API call that is a hash of the destination URL and other arguments combined with a secret known only to your server and application.
To expand upon this: suppose your API call has arguments foo, bar and qux. I would add a signature argument, the value of which could be something as simple as sorting the other arguments by name, concatenating them with their values, adding a secret, and hashing the lot. Then on the server side, I would do the same thing (excepting the signature argument) and check that the hash matches the one we were given in the request.
Consider authenticated HTTP.
For a cheaper alternative, there's shared secret/hash scheme. The client and the server have a shared secret string of text. Upon request, the client hashes together (using MD5, or SHA1, or SHA something else - you choose) the request fields and the secret. The hash value is attached to the request - say, as another POST field.
The server does the same operation with the request and with its copy of the secret, then compares the hash values. If they don't match - service denied.
For added security, you may encrypt the hash with a RSA public key. The client has the public key, the server keeps the private key. The server decrypts the hash with the private key, then the same. I did that with a C++ WinMobile client and a PHP-based service - works like a charm. No experience with crypto on iPhone, though.
UPDATE: now that I think of it, if we assume that the attacker has complete control over the client (ahem jailbroken iPhone and a debugger), the problem, as formulated above, is not solvable in theory. After all, the attacker might use your bits to access the service. Reverse-engineer the executable, find the relevant functions and call them with desired data. Build some global state, if necessary. Alternatively, they can automate your UI, screen scraper style. Such is the sad state of affairs.
Related
I'm writing REST API in PHP and recently I faced with authorization problem. I read a lot about basic authorization, about using private and public keys to create request signature. It is said that using request signature is more secure. But then I have a question:
-How should user will pass public key and generated signature?
I'm thinking about several options:
1) Create custom http header like X-Key, X-Signature
2) Use authorization header with custom scheme, like
AUTHORIZATION: SIGNATURE key='123' signature='abc'
3) Send this values as parameters. But I don't know if it acceptable for methods DELETE and PUT
What would you advice?
p.s. I don't want to implement oAuth
What are the desired properties of authentication scheme? Is this a publicly accessible or an intranet service? Are user accounts linked to something outside of scope of your API (linked 3rd party accounts etc). How are you going to distribute user credentials?
I would probably stick with plain old basic authorization, but encrypt everything at the transport level, making use of HTTPS mandatory. Rolling out your own cryptographic scheme is generally not a good idea. It's easier to fall victim to timing or replay attack than it seems. If you insist on client using a key pair for authentication, you can use HTTPS client certificates (though this is not widely used and maybe somewhat cumbersome solution).
There are a few security concerns about plain-text authentication over TLS. First, if someone implements MITM with forged certificate using either well known CA (maybe a government agency) or CA the client is forced to trust (big evil corporate proxy), they will get credentials. But you can't protect the client from its own environment anyway. Second, basic authentication can be prone to CSRF because browser knows how to do it and can remember credentials if you presented challenge and user filled the form. That's not a big problem if you adhere to REST principles and never allow state-changing GET requests. Also, if you are using JSON, never return arrays.
Let’s say I have a contract between two parties published on the Web. I want both parties to be able to sign the contract to show they consent to the terms, the way they would with handwriting in real life. I have seen many TOS agreements online where this is done with just a check box, but I want to go a step further and enable each party to assert that the signature is theirs and not a forgery (somebody else checking the box for them).
Assuming the page is already served via HTTPS and username/password combos are not an option, which cryptographic technology is best suited for identity validation: PGP, SSL, or something else?
How might I do this using only HTML and a LAMP server on the other end, in such a way that the process is as automated as possible while still being secure? Code samples are obviously welcome but not necessary; I’m just trying to conceptualize it: do the contents of the contract have to be included in the signature? Do I have the users upload public keys or something? I’m no crypto expert so that’s where I get lost.
SSL is a transport security mechanism, it's not applicable.
You can use OpenPGP or you can use PKI (X.509 certificates and CMS format). These technologies let you sign the data twice or more times without invalidating previous signatures - this is done by using detached signatures.
The choice of what (PGP or PKI) to use is yours - these technologies can be used in similar scenarios, but have different ways to authenticate keys: in PGP user keys are signed by other users, while in PKI certificates are signed by certificate authorities, which is supposed to have more credibility.
When you "sign the document" using cryptographic signature, from technical point of view it's a hash of the document that is signed. The hash can be calculated on the server and sent to the client for signing, then the detached signature is transferred back to the server. So you can keep the document on the server, and private keys used for signing will not leave the client.
However, to do actual signing on the client, you need some module which will communicate with the server and do the job. You can't go with just a web browser - some browser plug-in is required. The reason is that Javascript "cryptography", even if it technically allowed access to client-side keys stored in files or on cryptographic devices, has certain conceptual flaws which make it almost useless. So you end up with using something more trusted and secure, i.e. signed applet or ActiveX control or Flash script.
Our company provides various security components, among which there are components and modules for distributed signing (including above mentioned plugins). These modules are for PKI operations (though in general we also have components for OpenPGP operations, these components don't support distributed signing at the moment).
And I should note, that "automation" here is possible to extent when the user chooses the certificate to use and clicks "sign" button (for example). You can't sign anything without user's explicit action. In some cases the user would also need to provide a PIN / password which protects a private key from being misused.
I'd like to make requests from an iphone app to a web service I've built. How can I verify that requests made to the web service come from my iphone app (or indeed any authorised source) and are not forged?
I have looked at basic auth over HTTPS but is baking credentials into an application secure?
This question isn't really iphone specific; I'd like to know how to protect and authenticate requests in general.
Authentication can be asserted by presenting something you know, something you have, something you are or a combination of the three.
The iPhone doesn't have retinal or fingerprint scanners, so there are no "something you are" options available.
Client certificates work well as a "something you have" token. Most smartcards work by signing a message with an embedded certificate. When a certificate is compromised, it can be put onto a Certificate Revocation List (CRL) referenced by the webservers. Obviously, you wouldn't want to put your app's embedded certificate in the CRL -- that would deny access to all your users. Instead, you'll want users to download individual certificates to their iPhone.
After that, it's a matter of monitoring for unusual behavior to find the bad actors and adding those certs to the CRL. Two dead giveaways would be clients who send too many requests at once or from too many different IPs in too short a time.
Login/password is a simple "something you know" token. Like certificates, login/password combinations can be compromised and similar monitoring can be set up to find inappropriate behavior. The difference is compromised accounts would be marked "blocked" rather than added to a CRL.
By requiring both a client certificate and a login/password you increase the amount of effort needed to compromise an account.
Of course, you must ensure only valid accounts are added to the database. If there is an automated way to create new accounts and corresponding client certificates, then that account creation server/process becomes the easiest way for bad actors to create viable, unauthorized accounts. Requiring a real person to sign-off on accounts removes the automation process, but means a disgruntled or corrupt employee could create invalid accounts. Requiring a second person to counter-sign the account makes it harder for a single person to be an inside threat.
In short, ensuring high integrity of the clients is a process that can be made arbitrarily complex and expensive. What tools and processes you decide to deploy as the authentication scheme has to be balanced by the value of what it is protecting.
In theory, if you want the connection to be secure, the best is to have the client sign their request using a certificate. There are multiple resources about this. Look for "client certificate" on Google.
This example from Sun is in Java, but the concept is similar whatever the language.
PS: obviously, this doesn't prevent you from using other authentication methods such as passwords, etc...
PPS: Keep in mind that if someone manages to extract the certificate from your application, you are screwed either way ;-). You can imagine a store providing an individual certificate to each app and invalidating the certificates that are compromised.
The standard should solve the following Authentication challenges like-
Replay attacks
Man in the Middle
Plaintext attacks
Dictionary attacks
Brute force attacks
Spoofing by counterfeit servers
I have already looked at Amazon Web Services and that is one possibility. More importantly there seems to be two most common approaches:
Use apiKey which is encoded in a similar fashion like AWS but is a post parameter to a request
Use Http AuthenticationHeader and use a similar signature like AWS.
Signature is typically obtained by signing a date stamp with an encrypted shared secret. This signature is therefore passed either as an apiKey or in
the Http AuthenticationHeader.
I would like to know weigh both the options from the community, who may have used one or more and would also like to explore other options that I am not
considering. I would also use HTTPS to secure my services.
"authentication" means:
prove me you are who you say you are
"who you are" is the identity of an entity (person, computer user, software, server, etc...)
"identity" is an attribute unique to each entity (a dba would say primary key here)
so you must prove to have that unique attribute in a way or another.
When the entity here is an HTTP client, then HTTP Auth is the standardized way to prove to the server its unique identity (represented by what we call a user name).
It does not bother with the security of the channel, that's what the presentation layer (ie., SSL) is for, and requires a shared secret between the parts. "Shared secret" means that both parts must know it and no one else does. This implies the two parts trust each other on not disclosing the secret or taking appropriate measures if it gets disclosed (changing the secret, for example).
HTTP as a protocol does not include other ways to do authorization and leaves that at other layers. As an example, SSL can prove the identity of the two parties without sharing a secret via the use of a public key infrastructure (certificates and certification authorities).
In the end:
if it's ok for you to share a secret between the parties, you can use HTTP Auth for authentication and SSL to secure the channel. It's up to the parties to securely exchanging and storing the shared secret
if you don't want to share a secret, but the parties can agree on a common trusted third party, you can speak plain HTTP and use SSL for both securing the channel and proving the identity of one or both parties using a PKI (> certificates)
there are many other possibilities but this two are the most standard I can think of and should be compatible with most of the existing HTTP softwares/libraries/whatevers out there
home brew systems, while technically valid, will either break accepted standards or be ad-hoc (hence non standard) systems implemented at the application layer (to solve an issue that should be addressed at another layer, bah)
There are no ways to prove the uniqueness of something without agreeing on a shared secret (and keeping it secret) or agreeing to trust someone else to take care of that uniqueness (PKI). Everything else is just implementation details.
I'm not certain there is one standard. If there is it would likely be HTTP Auth, (Basic or Digest). Both of the aforementioned are pretty poor solutions.
AWS is a good example of how a "roll-your-own" auth solution could work, however, when you're talking about security/authentication, roll-your-own is usually a bad idea unless you're a security/crypto guru.
My preferred choice is actually just using Client Side Certificates. It takes care of the authentication and security process. No need for an API Key because the Cert itself identifies the client user.
Background:
I'm designing the authentication scheme for a REST web service. This doesn't "really" need to be secure (it's more of a personal project) but I want to make it as secure as possible as an exercise/learning experience. I don't want to use SSL since I don't want the hassle and, mostly, the expense of setting it up.
These SO questions were especially useful to get me started:
RESTful Authentication
Best Practices for securing a REST API / web service
Examples of the best SOAP/REST/RPC web APIs? And why do you like them? And what’s wrong with them?
I'm thinking of using a simplified version of Amazon S3's authentication (I like OAuth but it seems too complicated for my needs). I'm adding a randomly generated nonce, supplied by the server, to the request, to prevent replay attacks.
To get to the question:
Both S3 and OAuth rely on signing the request URL along with a few selected headers. Neither of them sign the request body for POST or PUT requests. Isn't this vulnerable to a man-in-the-middle attack, which keeps the url and headers and replaces the request body with any data the attacker wants?
It seems like I can guard against this by including a hash of the request body in the string that gets signed. Is this secure?
A previous answer only mentioned SSL in the context of data transfer and didn't actually cover authentication.
You're really asking about securely authenticating REST API clients. Unless you're using TLS client authentication, SSL alone is NOT a viable authentication mechanism for a REST API. SSL without client authc only authenticates the server, which is irrelevant for most REST APIs because you really want to authenticate the client.
If you don't use TLS client authentication, you'll need to use something like a digest-based authentication scheme (like Amazon Web Service's custom scheme) or OAuth 1.0a or even HTTP Basic authentication (but over SSL only).
These schemes authenticate that the request was sent by someone expected. TLS (SSL) (without client authentication) ensures that the data sent over the wire remains untampered. They are separate - but complementary - concerns.
For those interested, I've expanded on an SO question about HTTP Authentication Schemes and how they work.
REST means working with the standards of the web, and the standard for "secure" transfer on the web is SSL. Anything else is going to be kind of funky and require extra deployment effort for clients, which will have to have encryption libraries available.
Once you commit to SSL, there's really nothing fancy required for authentication in principle. You can again go with web standards and use HTTP Basic auth (username and secret token sent along with each request) as it's much simpler than an elaborate signing protocol, and still effective in the context of a secure connection. You just need to be sure the password never goes over plain text; so if the password is ever received over a plain text connection, you might even disable the password and mail the developer. You should also ensure the credentials aren't logged anywhere upon receipt, just as you wouldn't log a regular password.
HTTP Digest is a safer approach as it prevents the secret token being passed along; instead, it's a hash the server can verify on the other end. Though it may be overkill for less sensitive applications if you've taken the precautions mentioned above. After all, the user's password is already transmitted in plain-text when they log in (unless you're doing some fancy JavaScript encryption in the browser), and likewise their cookies on each request.
Note that with APIs, it's better for the client to be passing tokens - randomly generated strings - instead of the password the developer logs into the website with. So the developer should be able to log into your site and generate new tokens that can be used for API verification.
The main reason to use a token is that it can be replaced if it's compromised, whereas if the password is compromised, the owner could log into the developer's account and do anything they want with it. A further advantage of tokens is you can issue multiple tokens to the same developers. Perhaps because they have multiple apps or because they want tokens with different access levels.
(Updated to cover implications of making the connection SSL-only.)
Or you could use the known solution to this problem and use SSL. Self-signed certs are free and its a personal project right?
If you require the hash of the body as one of the parameters in the URL and that URL is signed via a private key, then a man-in-the-middle attack would only be able to replace the body with content that would generate the same hash. Easy to do with MD5 hash values now at least and when SHA-1 is broken, well, you get the picture.
To secure the body from tampering, you would need to require a signature of the body, which a man-in-the-middle attack would be less likely to be able to break since they wouldn't know the private key that generates the signature.
In fact, the original S3 auth does allow for the content to be signed, albeit with a weak MD5 signature. You can simply enforce their optional practice of including a Content-MD5 header in the HMAC (string to be signed).
http://s3.amazonaws.com/doc/s3-developer-guide/RESTAuthentication.html
Their new v4 authentication scheme is more secure.
http://docs.aws.amazon.com/general/latest/gr/signature-version-4.html
Remember that your suggestions makes it difficult for clients to communicate with the server. They need to understand your innovative solution and encrypt the data accordingly, this model is not so good for public API (unless you are amazon\yahoo\google..).
Anyways, if you must encrypt the body content I would suggest you to check out existing standards and solutions like:
XML encryption (W3C standard)
XML Security