dash icon indicating copy to clipboard operation
dash copied to clipboard

dcc.Store and encrypted props

Open nicolaskruchten opened this issue 7 years ago • 6 comments
trafficstars

We should have a better/safer way of storing data on the client than CSS-invisible html.Div components, for sensitive data that not all users should see/have access to. I propose a new dcc.Store component which wouldn't render in the DOM and which would transparently encrypt/decrypt its data-* props on the server.

#254 was a first cut at implementing this in dash.py and some design discussion is available there. It seems like the way forward here is to make the base component class extensible with server-side transform/inverse_transform extension points.

@chriddyp is there an existing issue that captures other needs/thoughts around the base component extension idea?

nicolaskruchten avatar May 30 '18 13:05 nicolaskruchten

@nicolaskruchten Can this be closed now that dash-core-components has a new Store component?

valentijnnieman avatar Oct 10 '18 16:10 valentijnnieman

No, because the data stored there is still unencrypted :)

nicolaskruchten avatar Oct 15 '18 01:10 nicolaskruchten

Hi guys,

Nice to see this discussion. This is exactly what I am looking for, a dcc.Store that has the capability to encrypt and decrypt its data, and it will probably help a lot of developers

supernyv avatar Apr 15 '24 15:04 supernyv

@AnnMarieW will dcc.Store do encryption these days?

gvwilson avatar Jun 03 '24 13:06 gvwilson

No, this feature is still not available, but I could see how it could be useful.

AnnMarieW avatar Jun 03 '24 14:06 AnnMarieW

Gonna pop in here, encryption is a good thought, but what has to do the decrypting?

  • if the clientside is doing the decryption, then you could capture the process of decryption by viewing the code
  • if the server, then there is the encrypted version that would be in the store and then the other side that would take the data, process and send it back for whatever it is used for.
  • if we are doing it server side, then what is the point of even sending the data to the client in its raw form? Wouldnt something like serverSide output work from dash-extensions?

A couple of notes:

  • if a dcc.Store is stored in memory, you cant view it unless you access the React props, but getting to this nested Store would be a bit of a chore, although possible.
  • it is possible for a developer to encrypt / decrypt in their own flows on the server side as well while using a dcc.Store, which means that the developer would be responsible for maintaining the secureness of their keys for the process

BSd3v avatar Jun 03 '24 14:06 BSd3v

cc @ndrezn what do you think?

gvwilson avatar Aug 07 '24 16:08 gvwilson

@nicolaskruchten could you explain your transform/inverse_transform idea a bit more...? Maybe there's a ticket somewhere I'm missing.


Though to be honest I'm not totally sure whether automatic encryption of dcc.Store props is super useful or significantly simplified by having it be automatic.

The use case I can imagine is if you want to store your complete dataset in dcc.Store but only show the end user a subset of that data (and therefore hide the non-shown data). But at this point a database backing the app is probably safer and easier to work with. But if there are other use cases I'm all ears.

Here's a small example showing something similar to @nicolaskruchten's PR implementation but in this case using callbacks + dcc.Store:

Code snippet
from dash import dcc, html, Input, Output, State, callback, Dash, no_update
from cryptography.fernet import Fernet
import base64

# Generate a key for encryption and decryption
key = Fernet.generate_key()
cipher_suite = Fernet(key)

app = Dash(__name__)

app.layout = html.Div(
    [
        dcc.Input(
            id="input-data",
            type="text",
            placeholder="Enter data to encrypt",
            style={"marginBottom": "10px"},
        ),
        html.Button("Submit", id="submit-button", n_clicks=0),
        dcc.Store(id="encrypted-store"),
        html.Div(id="output"),
    ]
)


@callback(
    Output("encrypted-store", "data"),
    Input("submit-button", "n_clicks"),
    State("input-data", "value"),
)
def encrypt_data(n_clicks, value):
    if n_clicks > 0 and value:
        encrypted_data = cipher_suite.encrypt(value.encode())
        # Encode to base64 to store in dcc.Store
        encoded_data = base64.b64encode(encrypted_data).decode()
        return encoded_data
    return no_update


@callback(Output("output", "children"), Input("encrypted-store", "data"))
def decrypt_data(encoded_data):
    if encoded_data:
        # Decode from base64 and decrypt
        encrypted_data = base64.b64decode(encoded_data)
        decrypted_data = cipher_suite.decrypt(encrypted_data).decode()
        return f"Encrypted data: {encoded_data}\n Decrypted data: {decrypted_data}"
    return "No data stored"


if __name__ == "__main__":
    app.run_server(debug=True)

ndrezn avatar Aug 07 '24 17:08 ndrezn

the transform/inverse thing was in a comment from Chris in my initial PR https://github.com/plotly/dash/pull/254

nicolaskruchten avatar Aug 07 '24 17:08 nicolaskruchten

not totally sure whether automatic encryption of dcc.Store props is super useful or significantly simplified by having it be automatic.

The idea here was to provide a first-class/recommended way of doing things that doesn't require users to copy-paste boilerplate around.

nicolaskruchten avatar Aug 07 '24 17:08 nicolaskruchten

For my own reference, @chriddyp puts the use case well in: https://community.plotly.com/t/writing-secure-dash-apps-community-thread/54619

  1. dcc.Store Data is accessible, even if it’s not visible

All of the data that is returned in a callback can be accessed by the current user of the app, even if that data isn’t necessarily visible.

So, be careful not to store secret or sensitive data in dcc.Store or other callbacks if you don’t intend all users to be able to access the data.

For example, if you have a dataframe that has several unused but sensitive columns (e.g. data about a particular client), then remove those columns before saving it in a dcc.Store. Even if your app doesn’t use those columns, the data is still accessible in the browser session.

ndrezn avatar Aug 12 '24 14:08 ndrezn

What if Plotly added support for Server Side stores, much like dash-extensions. Then you wouldnt have to worry about keeping things private.

Also, another thing to point out for a dcc.Store and encrypting on the server, you wouldnt be able to utilize the data on the browser via a clientside callback. So, the point of even having the data go to the client doesnt make much sense.

This of course would be different if for some reason you wanted to send the data via an api call by the client...

BSd3v avatar Aug 12 '24 15:08 BSd3v