GithubHelp home page GithubHelp logo

Comments (8)

nichochar avatar nichochar commented on June 18, 2024

This is very interesting. Here are a few thoughts:

  • This is slightly related to #32, which also deals with a security by adding a prefix
  • This is indeed a security flaw, but REDIS is designed "to be accessed by trusted clients inside trusted environments", and implementing this adds complexity to this project, which maybe should be implemented somewhere else
  • If we decide to encrypt the values, I think your solution has a good design. <secret><seperator><key> is a good way of making the store values ciphered, and the secret shouldn't be available in the store.

Overall, I think this is a good idea. I especially like that we can implement this without breaking API backwards compatibility, and essentially making it transparent to users.
If you were to make a PR, I will gladly review and consult.

I will also ask a member of our security team to take a look at this design

from snappass.

0x0ece avatar 0x0ece commented on June 18, 2024

I'm not sure I understand. Let's call secret what the user wants to share, and key the proposed encryption key.

Current solution is to store secret.
Proposed solution is to store key<separator>secret.

Am I understanding correctly?

If so, then I don't think the solution will add much in terms of security because anyone accessing key<separator>secret can separate, get key and use it to decrypt secret. (when reasoning about security, you need to assume the attacker knows the algorithms used, in particular here that the code is opensource).

The solution would be good if we could transmit key from userA to userB without storing it into Redis. This said, if we had such a channel, we could just use it to share secret in the first place.

from snappass.

devinlundberg avatar devinlundberg commented on June 18, 2024

As @nichochar pointed out, this was probably excluded from the original threat model of snappass, but it seems like an overall improvement.

Given the security architecture of redis, read access generally means write access (unless we are referring to backups). So we should also think about threats against availability and integrity. Not much we can do against an availability attack, but for integrity we could make it AEAD (with AES-GCM or something similar) and include the redis key id in the associated data. Not sure what a realistic attack would look like in the integrity arena and confidentiality is the most serious threat for this kind of system, but its still probably a good idea.

We should also note that attackers with access to browsing history of a targeted user and redis will be able to decrypt secrets (query parameters are stored in history). We can only enforce the exactly once nature of the system in the scenario where redis is compromised if there is a server side secret that we can assume is not compromised and is used in the encryption process and we are using authenticated encryption with the redis key in the associated data. The server side secret would need to be combined with the client side secret to derive a key using some sort of secure key derivation algorithm like shamir's secret sharing (probably not the ideal choice but a popular one).

We should make sure AEAD keys are generated using a secure random number generator (/dev/urandom or the like) and AEAD keys are never sent to the redis server.

from snappass.

nichochar avatar nichochar commented on June 18, 2024

Thanks for the input @0x0ece, @devinlundberg

So to sum up:

  • if we assume the attacker knows the algorithm, this really doesn't protect the password
  • if we assume they don't, or people are "looking into the values of the REDIS DB" by accident, then it adds a slight layer of obfuscation.

The question now is: do we think this is a tradeoff that is worth doing?

From what I can tell, there are no downsides, but the upsides are very slight. Does anybody have a strong opinion on whether this should be implemented, or not?

from snappass.

0x0ece avatar 0x0ece commented on June 18, 2024

We had a separated chat.

One possible solution is the following.
Generate a key which is never stored in Redis, but only passed between users. This can be an arbitrarily long random bit string.
Encrypt secret with key, e.g. using xor (one-time pad).

Store enc(secret) in Redis. In this way, an attacker accessing the db can't read any secret.

Transmit key to the other party, e.g. make it a param in the shared url. Note that an attacker reading the key could access the secret. The "protection" here comes from the fact that secret will be deleted after 1 consumption, so only one between the user and the attacker will get the secret, whoever arrives first.

In short, we're assuming that the channel between users is trusted enough to pass a short-lived key, but not longterm to transmit a secret that may be valid for more time. Think for instance to Slack, you trust it to share the key via chat, but you don't know what happens to their servers in the long term, so you don't want your actual secret to be stored there indefinitely.

Also, implementation caveat. If we expire the secret after 1 consumption, we need to make sure that the "channel" doesn't offer (or isn't allowed) to do url preview (again, think to Slack), for example via robots.txt or assuming snappass is on a private network.

from snappass.

samueldg avatar samueldg commented on June 18, 2024

Thanks all for the quick and thorough feedback!
I like the idea of server and client-side encryption, I had never thought of that...

@0x0ece I think you misunderstood my idea, it's probably because of my terminology.
Let me extend on your approach, and get some definitions in here :)

Sender - user that wants to share a secret/password;
Receiver - user Sender shares the link with;
secret - What Sender wants to share;
secret ID - Unique identifier of the secret;
encryption key - Key used to encrypt the secret, and decrypt the encrypted secret;
encrypted secret - Secret encrypted with the encryption key (also referred to as "cypher text");

Currently, the algorithm is:

  1. Sender sends the secret in a POST request to SnapPass;
  2. SnapPass randomly generates a unique secret ID;
  3. SnapPass stores the secret in plain text in Redis, with the secret ID as the key (key as in key-value!);
  4. SnapPass responds to Sender with a link containing the secret ID;
  5. Sender sends the link to Receiver;
  6. Receiver sends a GET request with the secret ID to SnapPass;
  7. SnapPass looks up the secret ID in Redis, retrieves the secret;
  8. SnapPass sends the secret to Receiver.

What is stored: secret
What is given back to Sender: secret ID

Contrast with my suggested implementation:

  1. Sender sends the secret in a POST request to SnapPass;
  2. SnapPass randomly generates a unique encryption key;
  3. SnapPass encrypts the secret with the encryption key, generating the encrypted secret
  4. SnapPass stores the encrypted secret in in Redis, with the secret ID as the key (key as in key-value!);
  5. SnapPass responds to Sender with a link containing both the secret ID and the encryption key;
    (That's where the separator comes in, as a way to have both in the link)
  6. Sender sends the link to Receiver;
  7. Receiver sends a GET request with the secret ID and encryption key to SnapPass;
  8. SnapPass lookup the secret ID in Redis, to retrieve the encrypted secret;
  9. SnapPass uses the encryption key to decrypt the encrypted secret, recovering the secret.
  10. SnapPass sends the secret to Receiver.

What is stored: encrypted secret
What is given to back Sender: secret ID + encryption key

This means people can know the algorithm, but they wouldn't be able to retrieve the value just from Redis.
What they need is the key, which SnapPass doesn't have.

An attacker would need to have both access to Redis and the link that was generated,
in order to get the password without expiring the link.
So it's not mere obfuscation, it's really encryption!

@devinlundberg I think this also addresses the browser history part (?), since the encryption key is unique for every request.
The link never makes it to Sender's browser history, and when it makes it into Receiver's browser history,
it has already been consumed and so it's too late!

It would however be possible for the attacker to hijack the browser and intercept the link,
but at this point there won't be any chance of securely sharing anything for poor Sender...

The value I saw to the addition, initially:

  • If an attacker breaks on my servers, a Redis dump won't reveal any data;
  • I don't have to rely on the fact that my sysadmin/public cloud employees/K8s cluster maintainers/etc. won't peep inside Redis;

from snappass.

samueldg avatar samueldg commented on June 18, 2024

@0x0ece I read your last message after posting mine, sigh. I think we are converging on a solution.

Some notes:

  • Passing an arbitrary long key in a URL might not work for all usages, because the length has an upper bound.
  • Consumption by URL preview and bots is indeed problematic, and was partially addressed in a previous PR (#46) for a number of known User-Agents. robots.txt is a pretty interesting addition though, since it protects more consistently for well-behaved robots. Another option is to use the app/API distinction I know is also being worked on. If you use a channel with URL preview, you can send a link to a page that will have a "reveal" button to show the secret after calling the API.

from snappass.

0x0ece avatar 0x0ece commented on June 18, 2024

@samueldg All you added LGTM.

from snappass.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.