Security of dash component ids (Can ids be changed in the browser for manipulation?)

Hello,

I am currently working on a project where I implemented authorization using the Flask-Login package. Based on the requested URL of the client I check if the user is authenticated/has the matching rights and return the corresponding page. I have pages the can be accessed with no authentication and pages that can only be accessed when the user is logged in and has specific rights.

In most cases, I display some data from a database. For example, I display buttons that allow a user/client to delete a specific row in a database table. For this, to work I save the id of the database row into the id of the dash component button. For example:

html.Button(
    "Click to delete the event",
    id={
        "type": "delete_event_btn",
        "index": database_event.id
    }
)

In my callback, I use pattern matching with the ALL / MATCH keyword to delete the row from the database table. For example:

@app.callback(
    Output("message_to_user", "children"),
    Input(Input({"type": "delete_event_btn", "index": ALL}, 'n_clicks'))
)
def delete_event(n_clicks):
    ctx = dash.callback_context
    
    if ctx.triggered is None:
        raise PreventUpdate
    # -----------------------------------------------------------
    # Check if user has rights to delete this event??????????????
    # -----------------------------------------------------------
    try:
        html_id_parsed = json.loads(ctx.triggered[0]['prop_id'].split('.')[0])
        event_id = html_id_parsed["index"]
    except Exception:
        raise PreventUpdate

    event = Events.query.filter_by(id=event_id).first()

    if event is None:
        raise PreventUpdate
    
    db.session.delete(event)
    db.session.commit()

    return "Event deleted"

My question is if I have to check if the user is authenticated and has the specific rights to actually delete the event/row from the database table?

As I mentioned earlier I only display the button if the user has the matching rights. So the question I ask could also be if the id of the HTML component in the browser can be changed using for example development tools? By doing this the callback could be tricked to delete a different event/row from the database table.

I tried out to change the id of the HTML component in the browser and it didn’t work. So when I clicked the button the id was still the same as before. But is there theoretically a way if you dig into the JavaScript code of Plotly Dash to change the id? Or maybe you could make a custom request to the server for faking/chaning the id? So are the ids of the Dash HTML components secure/can you trust them in the callbacks?

In this post, chriddyp mentioned that theoretically any value can be passed to a callback. So I guess I have to check in the callback if the user is authenticated and has the matching rights? I am not too sure about this.

Thank you for your help.

I’m using a different login process, but I think the process to verify authentication would be similar. As you noted, any value could be passed to a callback, so it makes sense to verify authentication before proceeding through a callback. One of my applications does this by checking for a valid username (stored in Redis) and an elastic expiration value that gets updated with each request. You could have a function that returns authentication status at the beginning of your callbacks. If authenticated, proceed. If not authenticated, stop processing or do something else.

# This callback updates the table for Scheduled Trips
@app.callback([Output('scheduled-trips-data', 'data'),
               Output('scheduled-trips-data', 'selected_rows')],
              [Input('hidden-cancel', 'children')])
def scheduled_trips(hidden_cancel):
    # Try to retrieve customerid
    customerid = session.get('username', 'NoID')

    # Check if customerid is invalid or if session is expired and prevent update if either are true
    if customerid == 'NoID' or verify_session_expiration() is False:
        raise PreventUpdate