flask
Advanced tools
| Security Considerations | ||
| ======================= | ||
| Web applications usually face all kinds of security problems and it's very | ||
| hard to get everything right. Flask tries to solve a few of these things | ||
| for you, but there are a couple more you have to take care of yourself. | ||
| .. _security-xss: | ||
| Cross-Site Scripting (XSS) | ||
| -------------------------- | ||
| Cross site scripting is the concept of injecting arbitrary HTML (and with | ||
| it JavaScript) into the context of a website. To remedy this, developers | ||
| have to properly escape text so that it cannot include arbitrary HTML | ||
| tags. For more information on that have a look at the Wikipedia article | ||
| on `Cross-Site Scripting | ||
| <https://en.wikipedia.org/wiki/Cross-site_scripting>`_. | ||
| Flask configures Jinja2 to automatically escape all values unless | ||
| explicitly told otherwise. This should rule out all XSS problems caused | ||
| in templates, but there are still other places where you have to be | ||
| careful: | ||
| - generating HTML without the help of Jinja2 | ||
| - calling :class:`~markupsafe.Markup` on data submitted by users | ||
| - sending out HTML from uploaded files, never do that, use the | ||
| ``Content-Disposition: attachment`` header to prevent that problem. | ||
| - sending out textfiles from uploaded files. Some browsers are using | ||
| content-type guessing based on the first few bytes so users could | ||
| trick a browser to execute HTML. | ||
| Another thing that is very important are unquoted attributes. While | ||
| Jinja2 can protect you from XSS issues by escaping HTML, there is one | ||
| thing it cannot protect you from: XSS by attribute injection. To counter | ||
| this possible attack vector, be sure to always quote your attributes with | ||
| either double or single quotes when using Jinja expressions in them: | ||
| .. sourcecode:: html+jinja | ||
| <input value="{{ value }}"> | ||
| Why is this necessary? Because if you would not be doing that, an | ||
| attacker could easily inject custom JavaScript handlers. For example an | ||
| attacker could inject this piece of HTML+JavaScript: | ||
| .. sourcecode:: html | ||
| onmouseover=alert(document.cookie) | ||
| When the user would then move with the mouse over the input, the cookie | ||
| would be presented to the user in an alert window. But instead of showing | ||
| the cookie to the user, a good attacker might also execute any other | ||
| JavaScript code. In combination with CSS injections the attacker might | ||
| even make the element fill out the entire page so that the user would | ||
| just have to have the mouse anywhere on the page to trigger the attack. | ||
| There is one class of XSS issues that Jinja's escaping does not protect | ||
| against. The ``a`` tag's ``href`` attribute can contain a `javascript:` URI, | ||
| which the browser will execute when clicked if not secured properly. | ||
| .. sourcecode:: html | ||
| <a href="{{ value }}">click here</a> | ||
| <a href="javascript:alert('unsafe');">click here</a> | ||
| To prevent this, you'll need to set the :ref:`security-csp` response header. | ||
| Cross-Site Request Forgery (CSRF) | ||
| --------------------------------- | ||
| Another big problem is CSRF. This is a very complex topic and I won't | ||
| outline it here in detail just mention what it is and how to theoretically | ||
| prevent it. | ||
| If your authentication information is stored in cookies, you have implicit | ||
| state management. The state of "being logged in" is controlled by a | ||
| cookie, and that cookie is sent with each request to a page. | ||
| Unfortunately that includes requests triggered by 3rd party sites. If you | ||
| don't keep that in mind, some people might be able to trick your | ||
| application's users with social engineering to do stupid things without | ||
| them knowing. | ||
| Say you have a specific URL that, when you sent ``POST`` requests to will | ||
| delete a user's profile (say ``http://example.com/user/delete``). If an | ||
| attacker now creates a page that sends a post request to that page with | ||
| some JavaScript they just have to trick some users to load that page and | ||
| their profiles will end up being deleted. | ||
| Imagine you were to run Facebook with millions of concurrent users and | ||
| someone would send out links to images of little kittens. When users | ||
| would go to that page, their profiles would get deleted while they are | ||
| looking at images of fluffy cats. | ||
| How can you prevent that? Basically for each request that modifies | ||
| content on the server you would have to either use a one-time token and | ||
| store that in the cookie **and** also transmit it with the form data. | ||
| After receiving the data on the server again, you would then have to | ||
| compare the two tokens and ensure they are equal. | ||
| Why does Flask not do that for you? The ideal place for this to happen is | ||
| the form validation framework, which does not exist in Flask. | ||
| .. _security-json: | ||
| JSON Security | ||
| ------------- | ||
| In Flask 0.10 and lower, :func:`~flask.jsonify` did not serialize top-level | ||
| arrays to JSON. This was because of a security vulnerability in ECMAScript 4. | ||
| ECMAScript 5 closed this vulnerability, so only extremely old browsers are | ||
| still vulnerable. All of these browsers have `other more serious | ||
| vulnerabilities | ||
| <https://github.com/pallets/flask/issues/248#issuecomment-59934857>`_, so | ||
| this behavior was changed and :func:`~flask.jsonify` now supports serializing | ||
| arrays. | ||
| Security Headers | ||
| ---------------- | ||
| Browsers recognize various response headers in order to control security. We | ||
| recommend reviewing each of the headers below for use in your application. | ||
| The `Flask-Talisman`_ extension can be used to manage HTTPS and the security | ||
| headers for you. | ||
| .. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman | ||
| HTTP Strict Transport Security (HSTS) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| Tells the browser to convert all HTTP requests to HTTPS, preventing | ||
| man-in-the-middle (MITM) attacks. :: | ||
| response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security | ||
| .. _security-csp: | ||
| Content Security Policy (CSP) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| Tell the browser where it can load various types of resource from. This header | ||
| should be used whenever possible, but requires some work to define the correct | ||
| policy for your site. A very strict policy would be:: | ||
| response.headers['Content-Security-Policy'] = "default-src 'self'" | ||
| - https://csp.withgoogle.com/docs/index.html | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy | ||
| X-Content-Type-Options | ||
| ~~~~~~~~~~~~~~~~~~~~~~ | ||
| Forces the browser to honor the response content type instead of trying to | ||
| detect it, which can be abused to generate a cross-site scripting (XSS) | ||
| attack. :: | ||
| response.headers['X-Content-Type-Options'] = 'nosniff' | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options | ||
| X-Frame-Options | ||
| ~~~~~~~~~~~~~~~ | ||
| Prevents external sites from embedding your site in an ``iframe``. This | ||
| prevents a class of attacks where clicks in the outer frame can be translated | ||
| invisibly to clicks on your page's elements. This is also known as | ||
| "clickjacking". :: | ||
| response.headers['X-Frame-Options'] = 'SAMEORIGIN' | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options | ||
| .. _security-cookie: | ||
| Set-Cookie options | ||
| ~~~~~~~~~~~~~~~~~~ | ||
| These options can be added to a ``Set-Cookie`` header to improve their | ||
| security. Flask has configuration options to set these on the session cookie. | ||
| They can be set on other cookies too. | ||
| - ``Secure`` limits cookies to HTTPS traffic only. | ||
| - ``HttpOnly`` protects the contents of cookies from being read with | ||
| JavaScript. | ||
| - ``SameSite`` restricts how cookies are sent with requests from | ||
| external sites. Can be set to ``'Lax'`` (recommended) or ``'Strict'``. | ||
| ``Lax`` prevents sending cookies with CSRF-prone requests from | ||
| external sites, such as submitting a form. ``Strict`` prevents sending | ||
| cookies with all external requests, including following regular links. | ||
| :: | ||
| app.config.update( | ||
| SESSION_COOKIE_SECURE=True, | ||
| SESSION_COOKIE_HTTPONLY=True, | ||
| SESSION_COOKIE_SAMESITE='Lax', | ||
| ) | ||
| response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax') | ||
| Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after | ||
| the given time, or the current time plus the age, respectively. If neither | ||
| option is set, the cookie will be removed when the browser is closed. :: | ||
| # cookie expires after 10 minutes | ||
| response.set_cookie('snakes', '3', max_age=600) | ||
| For the session cookie, if :attr:`session.permanent <flask.session.permanent>` | ||
| is set, then :data:`PERMANENT_SESSION_LIFETIME` is used to set the expiration. | ||
| Flask's default cookie implementation validates that the cryptographic | ||
| signature is not older than this value. Lowering this value may help mitigate | ||
| replay attacks, where intercepted cookies can be sent at a later time. :: | ||
| app.config.update( | ||
| PERMANENT_SESSION_LIFETIME=600 | ||
| ) | ||
| @app.route('/login', methods=['POST']) | ||
| def login(): | ||
| ... | ||
| session.clear() | ||
| session['user_id'] = user.id | ||
| session.permanent = True | ||
| ... | ||
| Use :class:`itsdangerous.TimedSerializer` to sign and validate other cookie | ||
| values (or any values that need secure signatures). | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie | ||
| .. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute | ||
| HTTP Public Key Pinning (HPKP) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| This tells the browser to authenticate with the server using only the specific | ||
| certificate key to prevent MITM attacks. | ||
| .. warning:: | ||
| Be careful when enabling this, as it is very difficult to undo if you set up | ||
| or upgrade your key incorrectly. | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning | ||
| Copy/Paste to Terminal | ||
| ---------------------- | ||
| Hidden characters such as the backspace character (``\b``, ``^H``) can | ||
| cause text to render differently in HTML than how it is interpreted if | ||
| `pasted into a terminal <https://security.stackexchange.com/q/39118>`__. | ||
| For example, ``import y\bose\bm\bi\bt\be\b`` renders as | ||
| ``import yosemite`` in HTML, but the backspaces are applied when pasted | ||
| into a terminal, and it becomes ``import os``. | ||
| If you expect users to copy and paste untrusted code from your site, | ||
| such as from comments posted by users on a technical blog, consider | ||
| applying extra filtering, such as replacing all ``\b`` characters. | ||
| .. code-block:: python | ||
| body = body.replace("\b", "") | ||
| Most modern terminals will warn about and remove hidden characters when | ||
| pasting, so this isn't strictly necessary. It's also possible to craft | ||
| dangerous commands in other ways that aren't possible to filter. | ||
| Depending on your site's use case, it may be good to show a warning | ||
| about copying code in general. |
+28
| Copyright 2010 Pallets | ||
| Redistribution and use in source and binary forms, with or without | ||
| modification, are permitted provided that the following conditions are | ||
| met: | ||
| 1. Redistributions of source code must retain the above copyright | ||
| notice, this list of conditions and the following disclaimer. | ||
| 2. Redistributions in binary form must reproduce the above copyright | ||
| notice, this list of conditions and the following disclaimer in the | ||
| documentation and/or other materials provided with the distribution. | ||
| 3. Neither the name of the copyright holder nor the names of its | ||
| contributors may be used to endorse or promote products derived from | ||
| this software without specific prior written permission. | ||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
| PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+65
| # Flask | ||
| Flask is a lightweight [WSGI][] web application framework. It is designed | ||
| to make getting started quick and easy, with the ability to scale up to | ||
| complex applications. It began as a simple wrapper around [Werkzeug][] | ||
| and [Jinja][], and has become one of the most popular Python web | ||
| application frameworks. | ||
| Flask offers suggestions, but doesn't enforce any dependencies or | ||
| project layout. It is up to the developer to choose the tools and | ||
| libraries they want to use. There are many extensions provided by the | ||
| community that make adding new functionality easy. | ||
| [WSGI]: https://wsgi.readthedocs.io/ | ||
| [Werkzeug]: https://werkzeug.palletsprojects.com/ | ||
| [Jinja]: https://jinja.palletsprojects.com/ | ||
| ## Installing | ||
| Install and update from [PyPI][] using an installer such as [pip][]: | ||
| ``` | ||
| $ pip install -U Flask | ||
| ``` | ||
| [PyPI]: https://pypi.org/project/Flask/ | ||
| [pip]: https://pip.pypa.io/en/stable/getting-started/ | ||
| ## A Simple Example | ||
| ```python | ||
| # save this as app.py | ||
| from flask import Flask | ||
| app = Flask(__name__) | ||
| @app.route("/") | ||
| def hello(): | ||
| return "Hello, World!" | ||
| ``` | ||
| ``` | ||
| $ flask run | ||
| * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) | ||
| ``` | ||
| ## Contributing | ||
| For guidance on setting up a development environment and how to make a | ||
| contribution to Flask, see the [contributing guidelines][]. | ||
| [contributing guidelines]: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst | ||
| ## Donate | ||
| The Pallets organization develops and supports Flask and the libraries | ||
| it uses. In order to grow the community of contributors and users, and | ||
| allow the maintainers to devote more time to the projects, [please | ||
| donate today][]. | ||
| [please donate today]: https://palletsprojects.com/donate |
+9
-8
@@ -14,12 +14,18 @@ import packaging.version | ||
| master_doc = "index" | ||
| default_role = "code" | ||
| extensions = [ | ||
| "sphinx.ext.autodoc", | ||
| "sphinx.ext.extlinks", | ||
| "sphinx.ext.intersphinx", | ||
| "sphinxcontrib.log_cabinet", | ||
| "sphinx_tabs.tabs", | ||
| "pallets_sphinx_themes", | ||
| "sphinx_issues", | ||
| "sphinx_tabs.tabs", | ||
| ] | ||
| autodoc_member_order = "bysource" | ||
| autodoc_typehints = "description" | ||
| autodoc_preserve_defaults = True | ||
| extlinks = { | ||
| "issue": ("https://github.com/pallets/flask/issues/%s", "#%s"), | ||
| "pr": ("https://github.com/pallets/flask/pull/%s", "#%s"), | ||
| } | ||
| intersphinx_mapping = { | ||
@@ -35,3 +41,2 @@ "python": ("https://docs.python.org/3/", None), | ||
| } | ||
| issues_github_path = "pallets/flask" | ||
@@ -62,6 +67,2 @@ # HTML ----------------------------------------------------------------- | ||
| # LaTeX ---------------------------------------------------------------- | ||
| latex_documents = [(master_doc, f"Flask-{version}.tex", html_title, author, "manual")] | ||
| # Local Extensions ----------------------------------------------------- | ||
@@ -68,0 +69,0 @@ |
+1
-1
@@ -57,3 +57,3 @@ .. rst-class:: hide-header | ||
| patterns/index | ||
| security | ||
| web-security | ||
| deploying/index | ||
@@ -60,0 +60,0 @@ async-await |
+2
-1
| BSD-3-Clause License | ||
| ==================== | ||
| .. include:: ../LICENSE.rst | ||
| .. literalinclude:: ../LICENSE.txt | ||
| :language: text |
+1
-1
@@ -162,3 +162,3 @@ Logging | ||
| for logger in ( | ||
| app.logger, | ||
| logging.getLogger(app.name), | ||
| logging.getLogger('sqlalchemy'), | ||
@@ -165,0 +165,0 @@ logging.getLogger('other_package'), |
+38
-53
| Metadata-Version: 2.1 | ||
| Name: Flask | ||
| Version: 3.0.2 | ||
| Version: 3.0.3 | ||
| Summary: A simple framework for building complex web applications. | ||
| Maintainer-email: Pallets <contact@palletsprojects.com> | ||
| Requires-Python: >=3.8 | ||
| Description-Content-Type: text/x-rst | ||
| Description-Content-Type: text/markdown | ||
| Classifier: Development Status :: 5 - Production/Stable | ||
@@ -19,2 +19,3 @@ Classifier: Environment :: Web Environment | ||
| Classifier: Topic :: Software Development :: Libraries :: Application Frameworks | ||
| Classifier: Typing :: Typed | ||
| Requires-Dist: Werkzeug>=3.0.0 | ||
@@ -32,14 +33,12 @@ Requires-Dist: Jinja2>=3.1.2 | ||
| Project-URL: Donate, https://palletsprojects.com/donate | ||
| Project-URL: Issue Tracker, https://github.com/pallets/flask/issues/ | ||
| Project-URL: Source Code, https://github.com/pallets/flask/ | ||
| Project-URL: Source, https://github.com/pallets/flask/ | ||
| Provides-Extra: async | ||
| Provides-Extra: dotenv | ||
| Flask | ||
| ===== | ||
| # Flask | ||
| Flask is a lightweight `WSGI`_ web application framework. It is designed | ||
| Flask is a lightweight [WSGI][] web application framework. It is designed | ||
| to make getting started quick and easy, with the ability to scale up to | ||
| complex applications. It began as a simple wrapper around `Werkzeug`_ | ||
| and `Jinja`_ and has become one of the most popular Python web | ||
| complex applications. It began as a simple wrapper around [Werkzeug][] | ||
| and [Jinja][], and has become one of the most popular Python web | ||
| application frameworks. | ||
@@ -52,68 +51,54 @@ | ||
| .. _WSGI: https://wsgi.readthedocs.io/ | ||
| .. _Werkzeug: https://werkzeug.palletsprojects.com/ | ||
| .. _Jinja: https://jinja.palletsprojects.com/ | ||
| [WSGI]: https://wsgi.readthedocs.io/ | ||
| [Werkzeug]: https://werkzeug.palletsprojects.com/ | ||
| [Jinja]: https://jinja.palletsprojects.com/ | ||
| Installing | ||
| ---------- | ||
| ## Installing | ||
| Install and update using `pip`_: | ||
| Install and update from [PyPI][] using an installer such as [pip][]: | ||
| .. code-block:: text | ||
| ``` | ||
| $ pip install -U Flask | ||
| ``` | ||
| $ pip install -U Flask | ||
| [PyPI]: https://pypi.org/project/Flask/ | ||
| [pip]: https://pip.pypa.io/en/stable/getting-started/ | ||
| .. _pip: https://pip.pypa.io/en/stable/getting-started/ | ||
| ## A Simple Example | ||
| A Simple Example | ||
| ---------------- | ||
| ```python | ||
| # save this as app.py | ||
| from flask import Flask | ||
| .. code-block:: python | ||
| app = Flask(__name__) | ||
| # save this as app.py | ||
| from flask import Flask | ||
| @app.route("/") | ||
| def hello(): | ||
| return "Hello, World!" | ||
| ``` | ||
| app = Flask(__name__) | ||
| ``` | ||
| $ flask run | ||
| * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) | ||
| ``` | ||
| @app.route("/") | ||
| def hello(): | ||
| return "Hello, World!" | ||
| .. code-block:: text | ||
| ## Contributing | ||
| $ flask run | ||
| * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) | ||
| Contributing | ||
| ------------ | ||
| For guidance on setting up a development environment and how to make a | ||
| contribution to Flask, see the `contributing guidelines`_. | ||
| contribution to Flask, see the [contributing guidelines][]. | ||
| .. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst | ||
| [contributing guidelines]: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst | ||
| Donate | ||
| ------ | ||
| ## Donate | ||
| The Pallets organization develops and supports Flask and the libraries | ||
| it uses. In order to grow the community of contributors and users, and | ||
| allow the maintainers to devote more time to the projects, `please | ||
| donate today`_. | ||
| allow the maintainers to devote more time to the projects, [please | ||
| donate today][]. | ||
| .. _please donate today: https://palletsprojects.com/donate | ||
| [please donate today]: https://palletsprojects.com/donate | ||
| Links | ||
| ----- | ||
| - Documentation: https://flask.palletsprojects.com/ | ||
| - Changes: https://flask.palletsprojects.com/changes/ | ||
| - PyPI Releases: https://pypi.org/project/Flask/ | ||
| - Source Code: https://github.com/pallets/flask/ | ||
| - Issue Tracker: https://github.com/pallets/flask/issues/ | ||
| - Chat: https://discord.gg/pallets | ||
+11
-6
| [project] | ||
| name = "Flask" | ||
| version = "3.0.2" | ||
| version = "3.0.3" | ||
| description = "A simple framework for building complex web applications." | ||
| readme = "README.rst" | ||
| license = {file = "LICENSE.rst"} | ||
| readme = "README.md" | ||
| license = {file = "LICENSE.txt"} | ||
| maintainers = [{name = "Pallets", email = "contact@palletsprojects.com"}] | ||
@@ -20,2 +20,3 @@ classifiers = [ | ||
| "Topic :: Software Development :: Libraries :: Application Frameworks", | ||
| "Typing :: Typed", | ||
| ] | ||
@@ -36,4 +37,3 @@ requires-python = ">=3.8" | ||
| Changes = "https://flask.palletsprojects.com/changes/" | ||
| "Source Code" = "https://github.com/pallets/flask/" | ||
| "Issue Tracker" = "https://github.com/pallets/flask/issues/" | ||
| Source = "https://github.com/pallets/flask/" | ||
| Chat = "https://discord.gg/pallets" | ||
@@ -98,2 +98,7 @@ | ||
| [tool.pyright] | ||
| pythonVersion = "3.8" | ||
| include = ["src/flask", "tests"] | ||
| typeCheckingMode = "basic" | ||
| [tool.ruff] | ||
@@ -103,3 +108,3 @@ src = ["src"] | ||
| show-fixes = true | ||
| show-source = true | ||
| output-format = "full" | ||
@@ -106,0 +111,0 @@ [tool.ruff.lint] |
| # | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # This file is autogenerated by pip-compile with Python 3.12 | ||
| # by the following command: | ||
@@ -7,7 +7,7 @@ # | ||
| # | ||
| build==1.0.3 | ||
| build==1.2.1 | ||
| # via -r build.in | ||
| packaging==23.2 | ||
| packaging==24.0 | ||
| # via build | ||
| pyproject-hooks==1.0.0 | ||
| # via build |
@@ -1,6 +0,5 @@ | ||
| -r docs.in | ||
| -r tests.in | ||
| -r typing.in | ||
| pip-tools | ||
| -r docs.txt | ||
| -r tests.txt | ||
| -r typing.txt | ||
| pre-commit | ||
| tox |
+117
-73
| # | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # This file is autogenerated by pip-compile with Python 3.12 | ||
| # by the following command: | ||
@@ -8,17 +8,23 @@ # | ||
| alabaster==0.7.16 | ||
| # via sphinx | ||
| asgiref==3.7.2 | ||
| # via | ||
| # -r tests.in | ||
| # -r typing.in | ||
| # -r docs.txt | ||
| # sphinx | ||
| asgiref==3.8.1 | ||
| # via | ||
| # -r tests.txt | ||
| # -r typing.txt | ||
| babel==2.14.0 | ||
| # via sphinx | ||
| build==1.0.3 | ||
| # via pip-tools | ||
| cachetools==5.3.2 | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| cachetools==5.3.3 | ||
| # via tox | ||
| certifi==2023.11.17 | ||
| # via requests | ||
| certifi==2024.2.2 | ||
| # via | ||
| # -r docs.txt | ||
| # requests | ||
| cffi==1.16.0 | ||
| # via cryptography | ||
| # via | ||
| # -r typing.txt | ||
| # cryptography | ||
| cfgv==3.4.0 | ||
@@ -29,40 +35,59 @@ # via pre-commit | ||
| charset-normalizer==3.3.2 | ||
| # via requests | ||
| click==8.1.7 | ||
| # via pip-tools | ||
| # via | ||
| # -r docs.txt | ||
| # requests | ||
| colorama==0.4.6 | ||
| # via tox | ||
| cryptography==41.0.7 | ||
| # via -r typing.in | ||
| cryptography==42.0.5 | ||
| # via -r typing.txt | ||
| distlib==0.3.8 | ||
| # via virtualenv | ||
| docutils==0.18.1 | ||
| docutils==0.20.1 | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| # sphinx-tabs | ||
| filelock==3.13.1 | ||
| filelock==3.13.3 | ||
| # via | ||
| # tox | ||
| # virtualenv | ||
| identify==2.5.33 | ||
| identify==2.5.35 | ||
| # via pre-commit | ||
| idna==3.6 | ||
| # via requests | ||
| # via | ||
| # -r docs.txt | ||
| # requests | ||
| imagesize==1.4.1 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| iniconfig==2.0.0 | ||
| # via pytest | ||
| # via | ||
| # -r tests.txt | ||
| # -r typing.txt | ||
| # pytest | ||
| jinja2==3.1.3 | ||
| # via sphinx | ||
| markupsafe==2.1.3 | ||
| # via jinja2 | ||
| mypy==1.8.0 | ||
| # via -r typing.in | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| markupsafe==2.1.5 | ||
| # via | ||
| # -r docs.txt | ||
| # jinja2 | ||
| mypy==1.9.0 | ||
| # via -r typing.txt | ||
| mypy-extensions==1.0.0 | ||
| # via mypy | ||
| # via | ||
| # -r typing.txt | ||
| # mypy | ||
| nodeenv==1.8.0 | ||
| # via pre-commit | ||
| packaging==23.2 | ||
| # via | ||
| # build | ||
| # -r typing.txt | ||
| # pre-commit | ||
| # pyright | ||
| packaging==24.0 | ||
| # via | ||
| # -r docs.txt | ||
| # -r tests.txt | ||
| # -r typing.txt | ||
| # pallets-sphinx-themes | ||
@@ -74,19 +99,22 @@ # pyproject-api | ||
| pallets-sphinx-themes==2.1.1 | ||
| # via -r docs.in | ||
| pip-tools==7.3.0 | ||
| # via -r dev.in | ||
| platformdirs==4.1.0 | ||
| # via -r docs.txt | ||
| platformdirs==4.2.0 | ||
| # via | ||
| # tox | ||
| # virtualenv | ||
| pluggy==1.3.0 | ||
| pluggy==1.4.0 | ||
| # via | ||
| # -r tests.txt | ||
| # -r typing.txt | ||
| # pytest | ||
| # tox | ||
| pre-commit==3.6.0 | ||
| pre-commit==3.7.0 | ||
| # via -r dev.in | ||
| pycparser==2.21 | ||
| # via cffi | ||
| pycparser==2.22 | ||
| # via | ||
| # -r typing.txt | ||
| # cffi | ||
| pygments==2.17.2 | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
@@ -96,60 +124,76 @@ # sphinx-tabs | ||
| # via tox | ||
| pyproject-hooks==1.0.0 | ||
| # via build | ||
| pytest==7.4.4 | ||
| # via -r tests.in | ||
| python-dotenv==1.0.0 | ||
| pyright==1.1.357 | ||
| # via -r typing.txt | ||
| pytest==8.1.1 | ||
| # via | ||
| # -r tests.in | ||
| # -r typing.in | ||
| # -r tests.txt | ||
| # -r typing.txt | ||
| python-dotenv==1.0.1 | ||
| # via | ||
| # -r tests.txt | ||
| # -r typing.txt | ||
| pyyaml==6.0.1 | ||
| # via pre-commit | ||
| requests==2.31.0 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| snowballstemmer==2.2.0 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| sphinx==7.2.6 | ||
| # via | ||
| # -r docs.in | ||
| # -r docs.txt | ||
| # pallets-sphinx-themes | ||
| # sphinx-issues | ||
| # sphinx-tabs | ||
| # sphinxcontrib-log-cabinet | ||
| sphinx-issues==3.0.1 | ||
| # via -r docs.in | ||
| sphinx-tabs==3.4.4 | ||
| # via -r docs.in | ||
| sphinx-tabs==3.4.5 | ||
| # via -r docs.txt | ||
| sphinxcontrib-applehelp==1.0.8 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| sphinxcontrib-devhelp==1.0.6 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| sphinxcontrib-htmlhelp==2.0.5 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| sphinxcontrib-jsmath==1.0.1 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| sphinxcontrib-log-cabinet==1.0.1 | ||
| # via -r docs.in | ||
| # via -r docs.txt | ||
| sphinxcontrib-qthelp==1.0.7 | ||
| # via sphinx | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| sphinxcontrib-serializinghtml==1.1.10 | ||
| # via sphinx | ||
| tox==4.12.0 | ||
| # via | ||
| # -r docs.txt | ||
| # sphinx | ||
| tox==4.14.2 | ||
| # via -r dev.in | ||
| types-contextvars==2.4.7.3 | ||
| # via -r typing.in | ||
| # via -r typing.txt | ||
| types-dataclasses==0.6.6 | ||
| # via -r typing.in | ||
| typing-extensions==4.9.0 | ||
| # via mypy | ||
| urllib3==2.1.0 | ||
| # via requests | ||
| virtualenv==20.25.0 | ||
| # via -r typing.txt | ||
| typing-extensions==4.11.0 | ||
| # via | ||
| # -r typing.txt | ||
| # mypy | ||
| urllib3==2.2.1 | ||
| # via | ||
| # -r docs.txt | ||
| # requests | ||
| virtualenv==20.25.1 | ||
| # via | ||
| # pre-commit | ||
| # tox | ||
| wheel==0.42.0 | ||
| # via pip-tools | ||
| # The following packages are considered to be unsafe in a requirements file: | ||
| # pip | ||
| # setuptools |
| pallets-sphinx-themes | ||
| sphinx | ||
| sphinx-issues | ||
| sphinxcontrib-log-cabinet | ||
| sphinx-tabs |
| # | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # This file is autogenerated by pip-compile with Python 3.12 | ||
| # by the following command: | ||
@@ -11,7 +11,7 @@ # | ||
| # via sphinx | ||
| certifi==2023.11.17 | ||
| certifi==2024.2.2 | ||
| # via requests | ||
| charset-normalizer==3.3.2 | ||
| # via requests | ||
| docutils==0.18.1 | ||
| docutils==0.20.1 | ||
| # via | ||
@@ -26,5 +26,5 @@ # sphinx | ||
| # via sphinx | ||
| markupsafe==2.1.3 | ||
| markupsafe==2.1.5 | ||
| # via jinja2 | ||
| packaging==23.2 | ||
| packaging==24.0 | ||
| # via | ||
@@ -47,9 +47,6 @@ # pallets-sphinx-themes | ||
| # pallets-sphinx-themes | ||
| # sphinx-issues | ||
| # sphinx-tabs | ||
| # sphinxcontrib-log-cabinet | ||
| sphinx-issues==3.0.1 | ||
| sphinx-tabs==3.4.5 | ||
| # via -r docs.in | ||
| sphinx-tabs==3.4.4 | ||
| # via -r docs.in | ||
| sphinxcontrib-applehelp==1.0.8 | ||
@@ -69,3 +66,3 @@ # via sphinx | ||
| # via sphinx | ||
| urllib3==2.1.0 | ||
| urllib3==2.2.1 | ||
| # via requests |
| # | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # This file is autogenerated by pip-compile with Python 3.12 | ||
| # by the following command: | ||
@@ -7,13 +7,13 @@ # | ||
| # | ||
| asgiref==3.7.2 | ||
| asgiref==3.8.1 | ||
| # via -r tests.in | ||
| iniconfig==2.0.0 | ||
| # via pytest | ||
| packaging==23.2 | ||
| packaging==24.0 | ||
| # via pytest | ||
| pluggy==1.3.0 | ||
| pluggy==1.4.0 | ||
| # via pytest | ||
| pytest==7.4.4 | ||
| pytest==8.1.1 | ||
| # via -r tests.in | ||
| python-dotenv==1.0.0 | ||
| python-dotenv==1.0.1 | ||
| # via -r tests.in |
| mypy | ||
| pyright | ||
| pytest | ||
| types-contextvars | ||
@@ -3,0 +5,0 @@ types-dataclasses |
| # | ||
| # This file is autogenerated by pip-compile with Python 3.11 | ||
| # This file is autogenerated by pip-compile with Python 3.12 | ||
| # by the following command: | ||
@@ -7,16 +7,28 @@ # | ||
| # | ||
| asgiref==3.7.2 | ||
| asgiref==3.8.1 | ||
| # via -r typing.in | ||
| cffi==1.16.0 | ||
| # via cryptography | ||
| cryptography==41.0.7 | ||
| cryptography==42.0.5 | ||
| # via -r typing.in | ||
| mypy==1.8.0 | ||
| iniconfig==2.0.0 | ||
| # via pytest | ||
| mypy==1.9.0 | ||
| # via -r typing.in | ||
| mypy-extensions==1.0.0 | ||
| # via mypy | ||
| pycparser==2.21 | ||
| nodeenv==1.8.0 | ||
| # via pyright | ||
| packaging==24.0 | ||
| # via pytest | ||
| pluggy==1.4.0 | ||
| # via pytest | ||
| pycparser==2.22 | ||
| # via cffi | ||
| python-dotenv==1.0.0 | ||
| pyright==1.1.357 | ||
| # via -r typing.in | ||
| pytest==8.1.1 | ||
| # via -r typing.in | ||
| python-dotenv==1.0.1 | ||
| # via -r typing.in | ||
| types-contextvars==2.4.7.3 | ||
@@ -26,3 +38,6 @@ # via -r typing.in | ||
| # via -r typing.in | ||
| typing-extensions==4.9.0 | ||
| typing-extensions==4.11.0 | ||
| # via mypy | ||
| # The following packages are considered to be unsafe in a requirements file: | ||
| # setuptools |
+10
-0
@@ -244,2 +244,12 @@ from __future__ import annotations | ||
| #: The Click command group for registering CLI commands for this | ||
| #: object. The commands are available from the ``flask`` command | ||
| #: once the application has been discovered and blueprints have | ||
| #: been registered. | ||
| self.cli = cli.AppGroup() | ||
| # Set the name of the Click group in case someone wants to add | ||
| # the app's commands to another CLI tool. | ||
| self.cli.name = self.name | ||
| # Add a static route using the provided static_url_path, static_host, | ||
@@ -246,0 +256,0 @@ # and static_folder if there is a configured static_folder. |
@@ -7,2 +7,3 @@ from __future__ import annotations | ||
| from .cli import AppGroup | ||
| from .globals import current_app | ||
@@ -12,2 +13,3 @@ from .helpers import send_from_directory | ||
| from .sansio.blueprints import BlueprintSetupState as BlueprintSetupState # noqa | ||
| from .sansio.scaffold import _sentinel | ||
@@ -19,2 +21,38 @@ if t.TYPE_CHECKING: # pragma: no cover | ||
| class Blueprint(SansioBlueprint): | ||
| def __init__( | ||
| self, | ||
| name: str, | ||
| import_name: str, | ||
| static_folder: str | os.PathLike[str] | None = None, | ||
| static_url_path: str | None = None, | ||
| template_folder: str | os.PathLike[str] | None = None, | ||
| url_prefix: str | None = None, | ||
| subdomain: str | None = None, | ||
| url_defaults: dict[str, t.Any] | None = None, | ||
| root_path: str | None = None, | ||
| cli_group: str | None = _sentinel, # type: ignore | ||
| ) -> None: | ||
| super().__init__( | ||
| name, | ||
| import_name, | ||
| static_folder, | ||
| static_url_path, | ||
| template_folder, | ||
| url_prefix, | ||
| subdomain, | ||
| url_defaults, | ||
| root_path, | ||
| cli_group, | ||
| ) | ||
| #: The Click command group for registering CLI commands for this | ||
| #: object. The commands are available from the ``flask`` command | ||
| #: once the application has been discovered and blueprints have | ||
| #: been registered. | ||
| self.cli = AppGroup() | ||
| # Set the name of the Click group in case someone wants to add | ||
| # the app's commands to another CLI tool. | ||
| self.cli.name = self.name | ||
| def get_send_file_max_age(self, filename: str | None) -> int | None: | ||
@@ -21,0 +59,0 @@ """Used by :func:`send_file` to determine the ``max_age`` cache |
+2
-4
@@ -232,4 +232,3 @@ from __future__ import annotations | ||
| module_name: str, app_name: str | None, raise_if_not_found: t.Literal[True] = True | ||
| ) -> Flask: | ||
| ... | ||
| ) -> Flask: ... | ||
@@ -240,4 +239,3 @@ | ||
| module_name: str, app_name: str | None, raise_if_not_found: t.Literal[False] = ... | ||
| ) -> Flask | None: | ||
| ... | ||
| ) -> Flask | None: ... | ||
@@ -244,0 +242,0 @@ |
@@ -30,8 +30,6 @@ from __future__ import annotations | ||
| @t.overload | ||
| def __get__(self, obj: None, owner: None) -> te.Self: | ||
| ... | ||
| def __get__(self, obj: None, owner: None) -> te.Self: ... | ||
| @t.overload | ||
| def __get__(self, obj: App, owner: type[App]) -> T: | ||
| ... | ||
| def __get__(self, obj: App, owner: type[App]) -> T: ... | ||
@@ -38,0 +36,0 @@ def __get__(self, obj: App | None, owner: type[App] | None = None) -> T | te.Self: |
@@ -43,2 +43,3 @@ """ | ||
| """ | ||
| from __future__ import annotations | ||
@@ -45,0 +46,0 @@ |
@@ -413,6 +413,2 @@ from __future__ import annotations | ||
| # Set the name of the Click group in case someone wants to add | ||
| # the app's commands to another CLI tool. | ||
| self.cli.name = self.name | ||
| def _check_setup_finished(self, f_name: str) -> None: | ||
@@ -419,0 +415,0 @@ if self._got_first_request: |
@@ -11,3 +11,2 @@ from __future__ import annotations | ||
| import click | ||
| from jinja2 import BaseLoader | ||
@@ -20,6 +19,8 @@ from jinja2 import FileSystemLoader | ||
| from .. import typing as ft | ||
| from ..cli import AppGroup | ||
| from ..helpers import get_root_path | ||
| from ..templating import _default_template_ctx_processor | ||
| if t.TYPE_CHECKING: # pragma: no cover | ||
| from click import Group | ||
| # a singleton sentinel value for parameter defaults | ||
@@ -71,2 +72,3 @@ _sentinel = object() | ||
| cli: Group | ||
| name: str | ||
@@ -103,8 +105,2 @@ _static_folder: str | None = None | ||
| #: The Click command group for registering CLI commands for this | ||
| #: object. The commands are available from the ``flask`` command | ||
| #: once the application has been discovered and blueprints have | ||
| #: been registered. | ||
| self.cli: click.Group = AppGroup() | ||
| #: A dictionary mapping endpoint names to view functions. | ||
@@ -111,0 +107,0 @@ #: |
@@ -280,2 +280,10 @@ from __future__ import annotations | ||
| def _lazy_sha1(string: bytes = b"") -> t.Any: | ||
| """Don't access ``hashlib.sha1`` until runtime. FIPS builds may not include | ||
| SHA-1, in which case the import and use as a default would fail before the | ||
| developer can configure something else. | ||
| """ | ||
| return hashlib.sha1(string) | ||
| class SecureCookieSessionInterface(SessionInterface): | ||
@@ -290,3 +298,3 @@ """The default session interface that stores sessions in signed cookies | ||
| #: the hash function to use for the signature. The default is sha1 | ||
| digest_method = staticmethod(hashlib.sha1) | ||
| digest_method = staticmethod(_lazy_sha1) | ||
| #: the name of the itsdangerous supported key derivation. The default | ||
@@ -293,0 +301,0 @@ #: is hmac. |
@@ -179,3 +179,3 @@ import datetime | ||
| test_uuid = uuid.UUID(bytes=b"\xDE\xAD\xBE\xEF" * 4) | ||
| test_uuid = uuid.UUID(bytes=b"\xde\xad\xbe\xef" * 4) | ||
| url = "/uuid_test" | ||
@@ -182,0 +182,0 @@ app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid)) |
@@ -20,18 +20,14 @@ from __future__ import annotations | ||
| @app.before_request | ||
| def before_sync() -> None: | ||
| ... | ||
| def before_sync() -> None: ... | ||
| @app.before_request | ||
| async def before_async() -> None: | ||
| ... | ||
| async def before_async() -> None: ... | ||
| @app.teardown_appcontext | ||
| def teardown_sync(exc: BaseException | None) -> None: | ||
| ... | ||
| def teardown_sync(exc: BaseException | None) -> None: ... | ||
| @app.teardown_appcontext | ||
| async def teardown_async(exc: BaseException | None) -> None: | ||
| ... | ||
| async def teardown_async(exc: BaseException | None) -> None: ... |
+17
-3
@@ -5,3 +5,3 @@ [tox] | ||
| pypy310 | ||
| py311-min | ||
| py312-min | ||
| py38-dev | ||
@@ -23,3 +23,3 @@ style | ||
| dev: -r requirements-skip/tests-dev.txt | ||
| commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs:tests} | ||
| commands = pytest -v --tb=short --basetemp={envtmpdir} {posargs} | ||
@@ -37,2 +37,16 @@ [testenv:style] | ||
| deps = -r requirements/docs.txt | ||
| commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html | ||
| commands = sphinx-build -W -b dirhtml docs docs/_build/dirhtml | ||
| [testenv:update-requirements] | ||
| deps = | ||
| pip-tools | ||
| pre-commit | ||
| skip_install = true | ||
| change_dir = requirements | ||
| commands = | ||
| pre-commit autoupdate -j4 | ||
| pip-compile -U build.in | ||
| pip-compile -U docs.in | ||
| pip-compile -U tests.in | ||
| pip-compile -U typing.in | ||
| pip-compile -U dev.in |
| Security Considerations | ||
| ======================= | ||
| Web applications usually face all kinds of security problems and it's very | ||
| hard to get everything right. Flask tries to solve a few of these things | ||
| for you, but there are a couple more you have to take care of yourself. | ||
| .. _security-xss: | ||
| Cross-Site Scripting (XSS) | ||
| -------------------------- | ||
| Cross site scripting is the concept of injecting arbitrary HTML (and with | ||
| it JavaScript) into the context of a website. To remedy this, developers | ||
| have to properly escape text so that it cannot include arbitrary HTML | ||
| tags. For more information on that have a look at the Wikipedia article | ||
| on `Cross-Site Scripting | ||
| <https://en.wikipedia.org/wiki/Cross-site_scripting>`_. | ||
| Flask configures Jinja2 to automatically escape all values unless | ||
| explicitly told otherwise. This should rule out all XSS problems caused | ||
| in templates, but there are still other places where you have to be | ||
| careful: | ||
| - generating HTML without the help of Jinja2 | ||
| - calling :class:`~markupsafe.Markup` on data submitted by users | ||
| - sending out HTML from uploaded files, never do that, use the | ||
| ``Content-Disposition: attachment`` header to prevent that problem. | ||
| - sending out textfiles from uploaded files. Some browsers are using | ||
| content-type guessing based on the first few bytes so users could | ||
| trick a browser to execute HTML. | ||
| Another thing that is very important are unquoted attributes. While | ||
| Jinja2 can protect you from XSS issues by escaping HTML, there is one | ||
| thing it cannot protect you from: XSS by attribute injection. To counter | ||
| this possible attack vector, be sure to always quote your attributes with | ||
| either double or single quotes when using Jinja expressions in them: | ||
| .. sourcecode:: html+jinja | ||
| <input value="{{ value }}"> | ||
| Why is this necessary? Because if you would not be doing that, an | ||
| attacker could easily inject custom JavaScript handlers. For example an | ||
| attacker could inject this piece of HTML+JavaScript: | ||
| .. sourcecode:: html | ||
| onmouseover=alert(document.cookie) | ||
| When the user would then move with the mouse over the input, the cookie | ||
| would be presented to the user in an alert window. But instead of showing | ||
| the cookie to the user, a good attacker might also execute any other | ||
| JavaScript code. In combination with CSS injections the attacker might | ||
| even make the element fill out the entire page so that the user would | ||
| just have to have the mouse anywhere on the page to trigger the attack. | ||
| There is one class of XSS issues that Jinja's escaping does not protect | ||
| against. The ``a`` tag's ``href`` attribute can contain a `javascript:` URI, | ||
| which the browser will execute when clicked if not secured properly. | ||
| .. sourcecode:: html | ||
| <a href="{{ value }}">click here</a> | ||
| <a href="javascript:alert('unsafe');">click here</a> | ||
| To prevent this, you'll need to set the :ref:`security-csp` response header. | ||
| Cross-Site Request Forgery (CSRF) | ||
| --------------------------------- | ||
| Another big problem is CSRF. This is a very complex topic and I won't | ||
| outline it here in detail just mention what it is and how to theoretically | ||
| prevent it. | ||
| If your authentication information is stored in cookies, you have implicit | ||
| state management. The state of "being logged in" is controlled by a | ||
| cookie, and that cookie is sent with each request to a page. | ||
| Unfortunately that includes requests triggered by 3rd party sites. If you | ||
| don't keep that in mind, some people might be able to trick your | ||
| application's users with social engineering to do stupid things without | ||
| them knowing. | ||
| Say you have a specific URL that, when you sent ``POST`` requests to will | ||
| delete a user's profile (say ``http://example.com/user/delete``). If an | ||
| attacker now creates a page that sends a post request to that page with | ||
| some JavaScript they just have to trick some users to load that page and | ||
| their profiles will end up being deleted. | ||
| Imagine you were to run Facebook with millions of concurrent users and | ||
| someone would send out links to images of little kittens. When users | ||
| would go to that page, their profiles would get deleted while they are | ||
| looking at images of fluffy cats. | ||
| How can you prevent that? Basically for each request that modifies | ||
| content on the server you would have to either use a one-time token and | ||
| store that in the cookie **and** also transmit it with the form data. | ||
| After receiving the data on the server again, you would then have to | ||
| compare the two tokens and ensure they are equal. | ||
| Why does Flask not do that for you? The ideal place for this to happen is | ||
| the form validation framework, which does not exist in Flask. | ||
| .. _security-json: | ||
| JSON Security | ||
| ------------- | ||
| In Flask 0.10 and lower, :func:`~flask.jsonify` did not serialize top-level | ||
| arrays to JSON. This was because of a security vulnerability in ECMAScript 4. | ||
| ECMAScript 5 closed this vulnerability, so only extremely old browsers are | ||
| still vulnerable. All of these browsers have `other more serious | ||
| vulnerabilities | ||
| <https://github.com/pallets/flask/issues/248#issuecomment-59934857>`_, so | ||
| this behavior was changed and :func:`~flask.jsonify` now supports serializing | ||
| arrays. | ||
| Security Headers | ||
| ---------------- | ||
| Browsers recognize various response headers in order to control security. We | ||
| recommend reviewing each of the headers below for use in your application. | ||
| The `Flask-Talisman`_ extension can be used to manage HTTPS and the security | ||
| headers for you. | ||
| .. _Flask-Talisman: https://github.com/GoogleCloudPlatform/flask-talisman | ||
| HTTP Strict Transport Security (HSTS) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| Tells the browser to convert all HTTP requests to HTTPS, preventing | ||
| man-in-the-middle (MITM) attacks. :: | ||
| response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security | ||
| .. _security-csp: | ||
| Content Security Policy (CSP) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| Tell the browser where it can load various types of resource from. This header | ||
| should be used whenever possible, but requires some work to define the correct | ||
| policy for your site. A very strict policy would be:: | ||
| response.headers['Content-Security-Policy'] = "default-src 'self'" | ||
| - https://csp.withgoogle.com/docs/index.html | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy | ||
| X-Content-Type-Options | ||
| ~~~~~~~~~~~~~~~~~~~~~~ | ||
| Forces the browser to honor the response content type instead of trying to | ||
| detect it, which can be abused to generate a cross-site scripting (XSS) | ||
| attack. :: | ||
| response.headers['X-Content-Type-Options'] = 'nosniff' | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options | ||
| X-Frame-Options | ||
| ~~~~~~~~~~~~~~~ | ||
| Prevents external sites from embedding your site in an ``iframe``. This | ||
| prevents a class of attacks where clicks in the outer frame can be translated | ||
| invisibly to clicks on your page's elements. This is also known as | ||
| "clickjacking". :: | ||
| response.headers['X-Frame-Options'] = 'SAMEORIGIN' | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options | ||
| .. _security-cookie: | ||
| Set-Cookie options | ||
| ~~~~~~~~~~~~~~~~~~ | ||
| These options can be added to a ``Set-Cookie`` header to improve their | ||
| security. Flask has configuration options to set these on the session cookie. | ||
| They can be set on other cookies too. | ||
| - ``Secure`` limits cookies to HTTPS traffic only. | ||
| - ``HttpOnly`` protects the contents of cookies from being read with | ||
| JavaScript. | ||
| - ``SameSite`` restricts how cookies are sent with requests from | ||
| external sites. Can be set to ``'Lax'`` (recommended) or ``'Strict'``. | ||
| ``Lax`` prevents sending cookies with CSRF-prone requests from | ||
| external sites, such as submitting a form. ``Strict`` prevents sending | ||
| cookies with all external requests, including following regular links. | ||
| :: | ||
| app.config.update( | ||
| SESSION_COOKIE_SECURE=True, | ||
| SESSION_COOKIE_HTTPONLY=True, | ||
| SESSION_COOKIE_SAMESITE='Lax', | ||
| ) | ||
| response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax') | ||
| Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after | ||
| the given time, or the current time plus the age, respectively. If neither | ||
| option is set, the cookie will be removed when the browser is closed. :: | ||
| # cookie expires after 10 minutes | ||
| response.set_cookie('snakes', '3', max_age=600) | ||
| For the session cookie, if :attr:`session.permanent <flask.session.permanent>` | ||
| is set, then :data:`PERMANENT_SESSION_LIFETIME` is used to set the expiration. | ||
| Flask's default cookie implementation validates that the cryptographic | ||
| signature is not older than this value. Lowering this value may help mitigate | ||
| replay attacks, where intercepted cookies can be sent at a later time. :: | ||
| app.config.update( | ||
| PERMANENT_SESSION_LIFETIME=600 | ||
| ) | ||
| @app.route('/login', methods=['POST']) | ||
| def login(): | ||
| ... | ||
| session.clear() | ||
| session['user_id'] = user.id | ||
| session.permanent = True | ||
| ... | ||
| Use :class:`itsdangerous.TimedSerializer` to sign and validate other cookie | ||
| values (or any values that need secure signatures). | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie | ||
| .. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute | ||
| HTTP Public Key Pinning (HPKP) | ||
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
| This tells the browser to authenticate with the server using only the specific | ||
| certificate key to prevent MITM attacks. | ||
| .. warning:: | ||
| Be careful when enabling this, as it is very difficult to undo if you set up | ||
| or upgrade your key incorrectly. | ||
| - https://developer.mozilla.org/en-US/docs/Web/HTTP/Public_Key_Pinning | ||
| Copy/Paste to Terminal | ||
| ---------------------- | ||
| Hidden characters such as the backspace character (``\b``, ``^H``) can | ||
| cause text to render differently in HTML than how it is interpreted if | ||
| `pasted into a terminal <https://security.stackexchange.com/q/39118>`__. | ||
| For example, ``import y\bose\bm\bi\bt\be\b`` renders as | ||
| ``import yosemite`` in HTML, but the backspaces are applied when pasted | ||
| into a terminal, and it becomes ``import os``. | ||
| If you expect users to copy and paste untrusted code from your site, | ||
| such as from comments posted by users on a technical blog, consider | ||
| applying extra filtering, such as replacing all ``\b`` characters. | ||
| .. code-block:: python | ||
| body = body.replace("\b", "") | ||
| Most modern terminals will warn about and remove hidden characters when | ||
| pasting, so this isn't strictly necessary. It's also possible to craft | ||
| dangerous commands in other ways that aren't possible to filter. | ||
| Depending on your site's use case, it may be good to show a warning | ||
| about copying code in general. |
-28
| Copyright 2010 Pallets | ||
| Redistribution and use in source and binary forms, with or without | ||
| modification, are permitted provided that the following conditions are | ||
| met: | ||
| 1. Redistributions of source code must retain the above copyright | ||
| notice, this list of conditions and the following disclaimer. | ||
| 2. Redistributions in binary form must reproduce the above copyright | ||
| notice, this list of conditions and the following disclaimer in the | ||
| documentation and/or other materials provided with the distribution. | ||
| 3. Neither the name of the copyright holder nor the names of its | ||
| contributors may be used to endorse or promote products derived from | ||
| this software without specific prior written permission. | ||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A | ||
| PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED | ||
| TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | ||
| PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF | ||
| LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING | ||
| NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
| SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
-80
| Flask | ||
| ===== | ||
| Flask is a lightweight `WSGI`_ web application framework. It is designed | ||
| to make getting started quick and easy, with the ability to scale up to | ||
| complex applications. It began as a simple wrapper around `Werkzeug`_ | ||
| and `Jinja`_ and has become one of the most popular Python web | ||
| application frameworks. | ||
| Flask offers suggestions, but doesn't enforce any dependencies or | ||
| project layout. It is up to the developer to choose the tools and | ||
| libraries they want to use. There are many extensions provided by the | ||
| community that make adding new functionality easy. | ||
| .. _WSGI: https://wsgi.readthedocs.io/ | ||
| .. _Werkzeug: https://werkzeug.palletsprojects.com/ | ||
| .. _Jinja: https://jinja.palletsprojects.com/ | ||
| Installing | ||
| ---------- | ||
| Install and update using `pip`_: | ||
| .. code-block:: text | ||
| $ pip install -U Flask | ||
| .. _pip: https://pip.pypa.io/en/stable/getting-started/ | ||
| A Simple Example | ||
| ---------------- | ||
| .. code-block:: python | ||
| # save this as app.py | ||
| from flask import Flask | ||
| app = Flask(__name__) | ||
| @app.route("/") | ||
| def hello(): | ||
| return "Hello, World!" | ||
| .. code-block:: text | ||
| $ flask run | ||
| * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) | ||
| Contributing | ||
| ------------ | ||
| For guidance on setting up a development environment and how to make a | ||
| contribution to Flask, see the `contributing guidelines`_. | ||
| .. _contributing guidelines: https://github.com/pallets/flask/blob/main/CONTRIBUTING.rst | ||
| Donate | ||
| ------ | ||
| The Pallets organization develops and supports Flask and the libraries | ||
| it uses. In order to grow the community of contributors and users, and | ||
| allow the maintainers to devote more time to the projects, `please | ||
| donate today`_. | ||
| .. _please donate today: https://palletsprojects.com/donate | ||
| Links | ||
| ----- | ||
| - Documentation: https://flask.palletsprojects.com/ | ||
| - Changes: https://flask.palletsprojects.com/changes/ | ||
| - PyPI Releases: https://pypi.org/project/Flask/ | ||
| - Source Code: https://github.com/pallets/flask/ | ||
| - Issue Tracker: https://github.com/pallets/flask/issues/ | ||
| - Chat: https://discord.gg/pallets |
Sorry, the diff of this file is too big to display
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
1510803
0.16%13632
0.27%