I'm developing a REST API but I am unable to use HTTPS without using self-signed certificates. I understand that that might be acceptable for some, but I don't want security messages popping up on client browsers.
The information being passed on the network is not sensitive, so I don't care about eavesdropping, but I want to make sure that only authorized clients are able to make requests and prevent replay attacks. Here's my idea:
A user is registered with the web application running the service. They want to register an API client, but they are not a developer, so they download a supplied client application (in this instance, an IC Card reader they will use with a computer that can communicate data read from the scanned cards to the web service over the API).
Step One: The API client application generates a key pair (e.g. OpenSSL) and sends the public key to the server (this can be a manual upload step when the user registers the client with the server). The server stores the public key in the database associated with the user and a description of the client (e.g. “IC Card Reader on PC01”).
Why do I want to use a key pair (like SSH)? I feel that a shared secret is vulnerable to eavesdropping as it has to be transferred over the network at least once. While this could be prevented by manually entering the key on the server's terminal rather than transporting it over the network, one design goal of the system is to allow users other than myself to register clients and get an API access token using the web interface for the application. Under this circumstance, an eavesdropper could intercept a shared secret key, while a keypair generated by the client would prevent that.
Step Two: When making a request the client first gets a nonce from the server (which stores it in a list or database of nonces and issued timestamps) and then makes the request including the nonce while signing the request with their private key. The server verifies that the message is authentic (was signed by the client and no one else) before executing the request.
My goal with this approach is to ensure that only valid clients are accessing the API, while also preventing replay attacks. An attacker should be able to see the contents of the messages (not really sensitive), but should be unable to modify them. The client doesn't really care whether the data coming from the server is authentic (because in this case the client is changing the state of the server, not the other way around).
- Does this approach meet these design goals?
- And does it prevent against man-in-the-middle attacks (assuming the original registration of the client was with the real server and not an attacker's fake one)?