Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Content-Security-Policy is blocking inline data #2344

Closed
cvergari opened this issue Mar 8, 2023 · 4 comments · Fixed by #2480
Closed

Content-Security-Policy is blocking inline data #2344

cvergari opened this issue Mar 8, 2023 · 4 comments · Fixed by #2480

Comments

@cvergari
Copy link

cvergari commented Mar 8, 2023

I am using flask admin and Talisman to implement CSP protection. CSP is blocking scripts (see #1839 and #1135 ), but I think it is also blocking in-line "data-source".

Here is an example code:

    from flask import Flask
    from flask_admin import Admin
    from flask_admin.contrib.sqla import ModelView
    from flask_sqlalchemy import SQLAlchemy
    from flask_talisman import Talisman

    app = Flask(__name__)
    app.config.update({
        "SECRET_KEY": "NOTASECRET",
        "SQLALCHEMY_DATABASE_URI": "sqlite:///:memory:",
        "SQLALCHEMY_TRACK_MODIFICATIONS": False
    })
    db = SQLAlchemy(app)


    csp = {
        'default-src': ['\'self\''],
        'script-src': ['\'self\''],
    }

    # Adding Talisman CSP protection
    talisman = Talisman(app, content_security_policy=csp)


    class Example(db.Model):
        __tablename__ = 'operator'
        id = db.Column(db.Integer, index=True, unique=True, primary_key=True)
        a_boolean = db.Column(db.Boolean())


    class EditableView(ModelView):
        column_editable_list = ['id', 'a_boolean']

    admin = Admin(app, url="/", template_mode="bootstrap4")
    admin.add_view(EditableView(Example, db.session, name="Examples"))


    @app.before_first_request
    def startup():
        db.create_all()
        db.session.add_all([Example(a_boolean=True),
                            Example(a_boolean=False)])
        db.session.commit()


    if __name__ == "__main__":
        app.run(debug=True)
  • Spuriois text appears at the bottom of the page ({"delete": "Are you sure you want to delete selected records?"})
  • Cannot modify boolean values inline
  • "Loading failed" appears in the boolean's edit menu
  • An error is print by flask's console: "GET /example/[%7B%22text%22:%20%22No%22,%20%22value%22:%20%22%22%7D,%20%7B%22text%22:%20%22Yes%22,%20%22value%22:%20%221%22%7D]?query=&_=1678272655129 HTTP/1.1" 404 -

And here is a screenshot of the errors I get:

Screenshot

Is there a workaround? I tried implementing safe hash as suggested in an answer to #1135, but I could not make it work.

@cvergari
Copy link
Author

cvergari commented May 5, 2023

I was able to fix the dropdown menu issue with this CSP policy string:

csp = {
    'default-src': ['\'self\''
                    ],
    'style-src': ['\'self\'',
                    'ajax.googleapis.com',
                    "'unsafe-hashes'",      # This line and the next allow loading some inline styles in flask admin
                   "'sha256-l/KYA9Q1I/ILRvd2rVApM7Asyv9CvBGrs03cA30BVGo='",
                   "'sha256-ZdHxw9eWtnxUb3mk6tBS+gIiVUPE3pGM470keHPDFlE='",
                   "'sha256-0EZqoz+oBhx7gF4nvY2bSqoGyy4zLjNF+SDQXGp/ZrY='",
                   "'sha256-NerDAUWfwD31YdZHveMrq0GLjsNFMwxLpZl0dPUeCcw='",
                   "'sha256-vYd+FsML43MBXhP+pXOhW9h0Cdq43hkCe4Im/yyvhss='",
                   ],
    'script-src': ['\'self\'',
                   'ajax.googleapis.com',
                   "'unsafe-eval'",
                   ],
}

However, this uses 'unsafe-eval',, which is not safe (as the name suggests). I really did not find a safe workaround. Flask-admin should drop using eval functions, but I don't know if that's even possible.

I am not sure I should close this issue, considering my solution is not completely satisfying.

@Zasgard
Copy link
Contributor

Zasgard commented Oct 18, 2023

The safe workaround I found was to implement nonces and then go into each and every template that had inline code, styles, and so on to apply the nonces. Its not ideal as it caused my template folder to overwrite a large portion of the original html pages but it was enough for me to not have to include "unsafe-eval". Instead I have the following:

response.headers['Content-Security-Policy'] = f"default-src 'self'; \
                script-src 'self' 'nonce-{nonce}'  \
            style-src 'self' 'nonce-{nonce}'\

Above is just an example.

@samuelhwilliams
Copy link
Contributor

I'm working on this in #2480.

Would be great to get your thoughts/opinions on whether this would work for you.

@Zasgard
Copy link
Contributor

Zasgard commented Jul 27, 2024

@samuelhwilliams I just took a look at your branch and it looks great! It's exactly what I did to get around the issue, by essentially passing a nonce everywhere that had inline code risks within the templates through global jinja variables. I didn't use Talisman like you did but I wish I had. Instead I generated the nonces before every request but everything else looks good based on the example. You did miss a few style blocks in your example that needed the nonce including a js file but I'm just nitpicking the example. It would definitely fix the use case I originally needed months ago.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 18, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Development

Successfully merging a pull request may close this issue.

3 participants