3

I'm trying to create a desktop application that will send updates to a web-service I am developing.

Ideally, the application would be configured only once and deployed to a network share. When configuring the application, the user will enter a password that is used to authenticate within the web-service. After that, a token will be created to be used on future connections.

This would allow any computer with access to the network share (even if it is not the computer in which the app was configured) to just run the application (which will connect to the web-service) without entering any credentials (because the token has been saved).

Question is: How should I protect this token?

  • I know that storing it client-side will never be completely secure, but I want to make it as hard as possible for someone to gain access to the plaintext token.
  • I'm looking for an answer that, preferably, does not depend on any operational-system resource (since the application can be executed from different devices).
  • Assume I have full control over the application and the web-server
  • I'm developing the console application using C#, but I believe this to be more of a theoretical question (not tied to any specific language)

Here are a few things I've tried/thought about:

  • Serializing the token using something like C#'s SecureString and storing it on a file: it's the best I've come with. But obviously, very trivial to reverse if someone gains access to the key file.

  • This answer suggests to use the Windows Data Protection API (DPAPI) (in C#, the ProtectedData class), but apparently, this would allow only the user who initially saved the credentials to access them, which would not work because I have to access the protected data from multiple users/devices.

  • Pass the token as a parameter to the application: this just changes where I'm going to store the token (on a batch file or OS task that calls the program, for example), but I don't think it makes it any more secure.

appa yip yip
  • 1,329
  • 1
  • 17
  • 35
  • 2
    Use tokens and refresh tokens. This way you can invalidate tokens. – CodeCaster Aug 09 '18 at 15:25
  • @CodeCaster I've updated the question. In this case, storing the token would be just like storing the password itself: someone could just copy it and use to connect to the web-service without authenticating, right? I'd like some elightment on how to _protect_ the password/token – appa yip yip Aug 09 '18 at 16:06
  • Can you define what you mean by "SO-independant"? The most common expansion for SO most people here will know is Stack Overflow, and I don't think you mean that. Unfortunately, you use it without defining it. – Damien_The_Unbeliever Aug 10 '18 at 08:39
  • @Damien_The_Unbeliever Thanks for noticing. It was a typo, what I meant was operational system (OS) independent. I've updated the question :) – appa yip yip Aug 10 '18 at 11:11
  • My last edit may have changed a bit too much about the question, but I believe that it now reflects best what I intended originally. – appa yip yip Aug 13 '18 at 18:35
  • Is the user an administrator of the machine? If not, then, you can hide a lot of things. You can also hide ProtectedData's additional entropy in your code. Not bulletprof, but works for "regular" users. – Simon Mourier Aug 16 '18 at 13:42
  • @SimonMourier No, in this specific case the users are not administrators of the machine – appa yip yip Aug 16 '18 at 13:44
  • So, make sure your app runs under different credentials than the logged user. Write another end-user app, just for setup, that talks to this app (using interprocess communication, TCPIP, whatever) only to gather credentials and send them to the first app. Write the token anywere the logged user has no access (protected data is fine since it will run in the app context, not in the logged user context) – Simon Mourier Aug 16 '18 at 13:55
  • @SimonMourier but would this work accross multiple computers (save the credential on one, and run the app on another)? – appa yip yip Aug 16 '18 at 16:21
  • If the app runs on a computer, it can use resources on this computer, whether it's started from a network or from a local disk. You don't talk about "running the app on another computer" – Simon Mourier Aug 16 '18 at 16:26
  • @SimonMourier Sorry, I thought it was clear with "this would allow any computer with access to the network share to just run the application", but I'll edit the question to make it easier to understand – appa yip yip Aug 16 '18 at 20:50
  • Tha's what I understood. It's pretty clear, but it does not change my answer. Running off a share or from the disk is the same. – Simon Mourier Aug 17 '18 at 06:35
  • @SimonMourier Oh, right! I've read the `ProtectedData's` docs again and realised I've misinterpreted something earlier. Can you post this as an answer so I can accept it? – appa yip yip Aug 17 '18 at 12:21

5 Answers5

1

Since the user is not an administrator of the machine (this is a fundamental hypothesis), there are many ways to hide things from her.

What I propose is:

  • Make sure the main app runs under different credentials than the logged user, a "special user".
  • Write another end-user app, just for setup, that talks to this app (using any interprocess communication you see fit, TCPIP, whatever, maybe secure but I wouldn't care too much at this). This app is used only to gather credentials and send them to the first app
  • Now, the main app can write the token anywhere the logged user has no access, but I recommand protected data because it's very easy to use

Here is some graphical explanation:

enter image description here

Since data encrypted using protected data (Windows Data Protection) can only be decrypted by the Windows user who has encrypted it, the logged on user will not be able to read the "special user" data.

Simon Mourier
  • 117,251
  • 17
  • 221
  • 269
0

I would suggest using JWT.

Your server would generate a Token after a successful authentication. The token would be sent to the client. Each subsequent call to the server would send the token in the header to the server. The server would then verify the token. If verified, the server knows the client has been authenticated.

No need to store usernames/passwords on the client.

birwin
  • 2,032
  • 2
  • 14
  • 35
  • How do you do the initial authentication to get the token without the client storing the credentials? – itsme86 Aug 09 '18 at 15:51
  • I missed that requirement in your original post. Sorry... JWT would probably only work if you configured the client with a token. – birwin Aug 09 '18 at 15:59
0

There is no bullet-proof way to solve this problem, the client finally needs access to the server, and so can do an attacker with enough privileges on the client.

What you describe is a typical scenario for the OAuth2 protocol, since you have control over the server. With OAuth2 you cannot avoid the need to store a secret on the client, but by exchanging it for a token, you can protect the original password and you can guarantee that the token is very strong (user passwords tends to be weak).

To store the token/password on the client one could encrypt it before storage, but this immediately raises the question of where to store the key for this encryption. An attacker could extract it from the client. There are some ways, which can improve security for the key storage.

  1. Depending on your situation you may consider the usage of a Hardware security module (HSM).
  2. You can use an OS specific key-store like you mentioned with the data-protection-api. A key-store can only help protecting a key, because it has the support of the operating system (I think this is what you meant with SO-independend?). With the DPAPI you can not only restrict access to the logged in user, rather you can restrict it to the local machine. Key-stores are available on other operating systems as well.
  3. Instead of storing the password on the client, it can be requested from the user each time the client starts. This can be reduced to the startup of the device, the password can then be hold exclusively in the memory.
martinstoeckli
  • 21,405
  • 4
  • 52
  • 78
0

Well, you can't protect anything in client side. Once it's there, anyone with privileged access can see it.

What you can do is make this piece of information useless without something else.

The best approach here would be to store a kind of public key in the client side and use this public key to create a hash to authenticate the user via webservice.

When the user configure the application, the server send this public key that the application stores locally. When the application call the server it will create a hash using this public key and a private key that is only known by the application and the server. Then the server can check if the hash is correct using its private key.

To improve security, you can use a timestamp in the hashing too, so it will change overtime and prevent the reuse of keys. Or it could send a new public key with the each webservice answer.

Ricardo BRGWeb
  • 2,756
  • 1
  • 15
  • 26
  • Where is the difference to storing a token locally? If I understand correctly you would store the _public key_ locally which has the same problem as storing a _key_ locally. – a-ctor Aug 15 '18 at 09:12
  • I assume you meant _signature_ instead of _hash_ because a has makes no sense with public and private keys ;) – a-ctor Aug 15 '18 at 09:13
  • @a-ctor the difference would be make it impossible to someone reverse engineer the hashing or reuse the same token outside the application. The terminology is not quite accurate, I agree... but what I meant was to have a hash that is stored locally in the user side that depends on a key that is inside the application and not visible in the user side... – Ricardo BRGWeb Aug 20 '18 at 14:02
0

I would suggest you to use IdentityServer4 as it provides RFC compliant protocols and depending on the GrantType a client application is given in your context your desktop application.

Even though the token is in plaintext it is protected in the webservice where the Access Control(Issuer of token) validates that a token is being received from the correct client by checking the Origin in the clients request and the one stored in the database.