GibbsConsulting / django-plotly-dash

Expose plotly dash apps as django tags
MIT License
552 stars 125 forks source link

Bypassing view access control using direct link #77

Closed brylie closed 5 years ago

brylie commented 5 years ago

When using the dash_app template tag, the contents are rendered in an iframe. A curious user can inspect the page source and get the direct link to the Dash app directly form the iframe. The direct link might allow the user to bypass access control and other precautions built into a project. This seems to undermine the view based access control:

Each view delegated through to plotly_dash can be wrapped using a view decoration function. This enables access to be restricted to logged-in user

How can we mitigate the risk of potential abuse, particularly when dashboards may contain sensitive data?

brylie commented 5 years ago

One fleeting thought might be to create a sub-class of DjangoDash, and use a mixin to enforce access control. This would probably require additions to the DjangoDash class. Taking this thought a bit further, perhaps DjangoDash could inherit from one of the django built-in class-based views?

eddy-ojb commented 5 years ago

Been thinking about this myself. I haven't managed to solve this yet generically but I guess it isn't a problem exclusive to DPD. Perhaps checking authentication in the middleware for all dash apps is an option so that nothing no views can render before authentication and authorization has occurred.

delsim commented 5 years ago

There is this configuration option which allows you to specify a function to call on every dash method invocation. This could be as simple as the existing login_required provided by Django or something more sophisticated.

There is a related discussion in #56 also.

brylie commented 5 years ago

@delsim would that work for users who know the direct link to the iframe src?

delsim commented 5 years ago

It should do, as all the iframe will do is use the django-plotly-dash urls, and if they are wrapped in some form of protection it doesn't matter where the request comes from.

brylie commented 5 years ago

My concern, which may be based on the wrong assumption, is that the django_plotly_dash view function may not use any access control decorators, even though my template view would use access control. So, by directly linking to the iframe source URL, a user could bypass access control, auditing, etc:

screenshot_20181203_113544

In this regard, the plotly_direct template tag would seem like the safer bet, since the dashboard is rendered directly into the (decorated) view template.

GibbsConsulting commented 5 years ago

Resolution of #82 will hopefully help.

brylie commented 5 years ago

I am able to confirm my suspicion that Dash views are not secured, even though the django view might use access control (such as @login_required). Steps to reproduce:

  1. create a simple Dash dashboard
  2. create a django view/url to display the Dash dashboard
  3. add @login_required to django view
  4. access the view as an authenticated user
  5. view console request log to get URL of django_plotly_dash/app/<app-name>/_dash-layout
  6. log out
  7. paste the URL into web browser and go

The Dash layout object can be viewed in full, including any possibly sensitive data.

flameshot-2018-12-12t14 18 48

For clarification, we are using Dash for internal dashboards, and want to be really cautious about user data, particularly in light of GDPR and respecting user privacy.

eddy-ojb commented 5 years ago

@brylie is this using an iframe or when returned as html? Or either? I will try this tomorrow

eddy-ojb commented 5 years ago

I am surprised this occurs given the @login_required decorator. Perhaps the middleware is a better option. I will also try this option tomorrow.

GibbsConsulting commented 5 years ago

Are you setting the PLOTLY_DASH.view_decorator option? If not then you would need to wrap pretty much all of the routes in the urls.py file.

In advance of the still-to-be written documentation (#82) a value of django_plotly_dash.access.login_required should make all dash apps require authentication.

brylie commented 5 years ago

Are you setting the PLOTLY_DASH.view_decorator option?

Yes, I have the following in my settings.py:

from django.contrib.auth.decorators import login_required

PLOTLY_DASH = {
    # Name of view wrapping function
    "view_decorator": login_required,
}
brylie commented 5 years ago

Thanks for clarifying the documentation-in-progress. It should also be mentioned that I would like to support specific access control rules per view, perhaps using group based access control. I.e. not all users should have access to all data/views, so simply checking login_required will not likely be sufficient.

brylie commented 5 years ago

is this using an iframe or when returned as html? Or either? I will try this tomorrow

@eddy-ojb I have tested the above scenario with plotly_direct, and then using the Chrome page inspector to get the XHR paths:

{% block dashboard %}
    {% plotly_direct name="TopUsers"%}
{% endblock dashboard %}

flameshot-2018-12-14t10 48 22

When I switch the template to use plotly_app, I am still able to access the iframe content directly as an anonymous user, by copying the value from src attiribute and pasting it in an incognito tab:

{% block dashboard %}
    {% plotly_app name="TopUsers"%}
{% endblock dashboard %}

flameshot-2018-12-14t10 57 15

GibbsConsulting commented 5 years ago

Are you able to try this:

PLOTLY_DASH = {
    # Name of view wrapping function
    "view_decorator": "django_plotly_dash.access.login_required",
}
brylie commented 5 years ago

Ah, sorry I overlooked that in your previous comment. I will try ASAP.

It would also be useful to know how to write custom access functions, so that I can, for example, check for group membership or other criteria before users can access a Dash view. Also, it would be good to share these authorization functions between the Dash and django view, for DRY code.

GibbsConsulting commented 5 years ago

Just pulled #90 into master, which hopefully addresses this, and closing this issue. Please reopen with comments if the documentation can be improved.

brylie commented 4 years ago

@GibbsConsulting does the login_required decorator prevent someone from directly accessing the underlying Dash app that is embedded via the IFrame? For example, say an authorized user logs in and copies the Dash app IFrame link and share it with an unauthorized person. Would that unauthorized person be able to use the direct link to the access Dash app, bypassing the Django access control?

brylie commented 4 years ago

I just want to be really clear here, because we are again looking at Django Plotly Dash as an alternative to Tableau.

GibbsConsulting commented 4 years ago

@brylie The function supplied as the view_decorator should get called on every call that passes through django_plotly_dash, so you should not be able to subvert the access control by copying the link.

You can verify this by doing exactly as you describe - go to an app as an authorized user, grab one of the urls (the debugging console of your browser can help you do this), and then try to access that url as an unauthorized user.

jeffamaxey commented 2 years ago

@brylie @GibbsConsulting I am running into a similar issue during penetration testing regarding the accessibility of underlying data via iframe URL.

Azure Active Directory was used to wrap all views in my core django project, and I have also passed a view decorator in PLOTLY_DASH as specified above. Would you be able to share how you implemented it?

GibbsConsulting commented 2 years ago

@jeffamaxey can you describe the particular issue you're facing? Ideally by opening a new issue as that would be easier to track.