Security for browser forms
This package contains utilities that can help protect parts of Plone or applications build on top of the Plone framework.
1. Restricting to HTTP POST
a) Using decorator
If you only need to allow HTTP POST requests you can use the PostOnly checker:
from plone.protect import PostOnly from plone.protect import protect @protect(PostOnly) def manage_doSomething(self, param, REQUEST=None): pass
This checker operates only on HTTP requests; other types of requests are not checked.
b) Passing request to a function validator
from plone.protect import PostOnly ... PostOnly(self.context.REQUEST) ...
2. Form authentication (CSRF)
A common problem in web applications is Cross Site Request Forgery or CSRF. This is an attack method in which an attacker tricks a browser to do a HTTP form submit to another site. To do this the attacker needs to know the exact form parameters. Form authentication is a method to make it impossible for an attacker to predict those parameters by adding an extra authenticator which can be verified.
Generating the token
To use the form authenticator you first need to insert it into your form. This can be done using a simple TAL statement inside your form:
<span tal:replace="structure context/@@authenticator/authenticator"/>
this will produce a HTML input element with the authentication information.
If you want to create the token value programmatically, use the following:
from plone.protect.authenticator import createToken token = createToken()
Validating the token
a) Zope Component Architecture way
Next you need to add logic somewhere to verify the authenticator. This can be done using a call to the authenticator view. For example:
authenticator=getMultiAdapter((context, request), name=u"authenticator") if not authenticator.verify(): raise Unauthorized
b) Using decorator
You can do the same thing more conveniently using the protect decorator:
from plone.protect import CheckAuthenticator from plone.protect import protect @protect(CheckAuthenticator) def manage_doSomething(self, param, REQUEST=None): pass
c) Passing request to a function validator
from plone.protect import CheckAuthenticator ... CheckAuthenticator(self.context.REQUEST) ...
You can also pass in the token by using the header X-CSRF-TOKEN. This can be useful for AJAX requests.
The most common way to use plone.protect is through the protect decorator. This decorator takes a list of checkers as parameters: each checker will check a specific security aspect of the request. For example:
from plone.protect import protect from plone.protect import PostOnly @protect(PostOnly) def SensitiveMethod(self, REQUEST=None): # This is only allowed with HTTP POST requests.
This relies on the protected method having a parameter called REQUEST (case sensitive).
Customized Form Authentication
If you’d like use a different authentication token for different forms, you can provide an extra string to use with the token:
<tal:authenticator tal:define="authenticator context/@@authenticator"> <span tal:replace="structure python: authenticator.authenticator('a-form-related-value')"/> </tal:authenticator>
authenticator=getMultiAdapter((context, request), name=u"authenticator") if not authenticator.verify('a-form-related-value'): raise Unauthorized
With the decorator:
from plone.protect import CustomCheckAuthenticator from plone.protect import protect @protect(CustomCheckAuthenticator('a-form-related-value')) def manage_doSomething(self, param, REQUEST=None): pass
Automatic CSRF Protection
Since version 3, plone.protect provides automatic CSRF protection. It does this by automatically including the auth token to all internal forms when the user requesting the page is logged in.
Additionally, whenever a particular request attempts to write to the ZODB, it’ll check for the existence of a correct auth token.
Allowing write on read programmatically
When you need to allow a known write on read, you’ve got several options.
If you just want to allow an object to be writable on a request…
You can use the safeWrite helper function:
from plone.protect.auto import safeWrite safeWrite(myobj, request)
Marking the entire request as safe
Just add the IDisableCSRFProtection interface to the current request object:
from plone.protect.interfaces import IDisableCSRFProtection from zope.interface import alsoProvides alsoProvides(request, IDisableCSRFProtection)
Warning! When you do this, the current request is susceptible to CSRF exploits so do any required CSRF protection manually.
plone.protect also provides, by default, clickjacking protection since version 3.0.
To protect against this attack, Plone uses the X-Frame-Options header. plone.protect will set the X-Frame-Options value to SAMEORIGIN.
To customize this value, you can set it to a custom value for a custom view (e.g. self.request.response.setHeader('X-Frame-Options', 'ALLOWALL')), override it at your proxy server, or you can set the environment variable of PLONE_X_FRAME_OPTIONS to whatever value you’d like plone.protect to set this to globally.
You can opt out of this by making the environment variable empty.
Disable All Automatic CSRF Protection
To disable all automatic CSRF protection, set the environment variable PLONE_CSRF_DISABLED value to true.
It is very dangerous to do this. Do not do this unless the ZEO client with this setting is not public and you know what you are doing.
This doesn’t disable explicit and manual CSRF protection checks.
Fixing CSRF Protection failures in tests
If you get Unauthorized errors in tests due to unprotected form submission where normally automatic protection would be in place you can use the following blueprint to protect your forms:
from plone.protect.authenticator import createToken from ..testing import MY_INTEGRATION_TESTING_LAYER import unittest class MyTest(unittest.TestCase): layer = MY_INTEGRATION_TESTING_LAYER def setUp(self): self.request = self.layer['request'] # Disable plone.protect for these tests self.request.form['_authenticator'] = createToken() # Eventuelly you find this also useful self.request.environ['REQUEST_METHOD'] = 'POST'
This package monkey patches a number of modules in order to better handle CSRF protection:
- Archetypes add forms, add csrf - Zope2 object locking support - pluggable auth csrf protection
If you are using a proxy cache in front of your site, be aware that you will need to clear the entry for ++resource++protect.js every time you update this package or you will find issues with modals while editing content.
plone.protect version 3 was made for Plone 5. You can use it on Plone 4 for better protection, but you will need the plone4.csrffixes hotfix package as well to avoid getting needless warnings or errors. See the hotfix announcement and the hotfix page.
- Avoid deprecation warnings. [gforcada] (#87)
- Remove five.globalrequest dependency. It has been deprecated upstream (Zope 4). [gforcada]
- Adapt to changed visibility of buildfacade in AccessControl.requestmethod. Requires AccessControl >= 4.0b6 [tschorr]
- More Python 2 / 3 compatibility [pbauer, MatthewWilkes]
- Fix marmoset monkey patching for Python 3 [jensens]
- Don’t patch until zcml loaded [davisagli]
- Put the marmoset on a leash (reset csrf-checks after tests) [davisagli]
- Fix package dependencies; cssselect has been an extra of lxml since 2014 (closes #79). [hvelarde]
- Fixed tests to work with merged plone.login [jensens]
- Version 3.1.3 introduced a Python 3 compatibility fix that broke some Python 2 versions with a SyntaxError. Reports are mostly for Python 2.7.8 and lower, but also one for 2.7.14, but only on Travis. So this marks a breaking change. The incompatibility will be reverted on branch 3.x. Version 3.1.4 should be safe to use again. See issue 74. and issue 75. [maurits]
- Avoid CSRF warnings due to generating image scales stored in a plone.scale.storage.ScalesDict. [davisagli]
- More Python 2 / 3 compatibility. Warning: this gives a SyntaxError on Python 2.7.8 or lower. See issue 74. [pbauer]
- Transform does not log a warning for empty responses (Fixes https://github.com/plone/plone.protect/issues/15) [fRiSi]
- Add Python 2 / 3 compatibility [vincero]
- README wording tweaks [tkimnguyen]
- Log forbidden URLs. Fixes https://github.com/plone/plone.protect/issues/66 [gforcada]
- Catch AttributeError on transform. [hvelarde]
- Fix logging to no longer write traceback to stdout, but include it in the logging message instead. [jone]
- Remove unittest2 dependency [kakshay21]
- Allow confirm-action for all contexts, instead of only Plone Site root. This avoids an error when calling it on a subsite. Fixes issue #51. [maurits]
- Code Style: utf8-headers, import sorting, new style namespace declaration, autopep8 [jensens]
- Fix #57: Html must contain “body”, otherwise plone.protect breaks. [jensens]
- avoid zope.globalrequest.getRequest() [tschorr]
- Avoid regenerating image scale over and over in Plone 4. Avoid (unnoticed) error when refreshing lock in Plone 4, plus a few other cases that were handled by plone4.csrffixes. Fixes https://github.com/plone/plone.protect/issues/47 [maurits]
- Only try the confirm view for urls that are in the portal. This applies PloneHotfix20160830. [maurits]
- Removed RedirectTo patch. The patch has been merged to Products.CMFFormController 3.0.7 (Plone 4.3 and 5.0) and 3.1.2 (Plone 5.1). Note that we are not requiring those versions in our setup.py, because the code in this package no longer needs it. [maurits]
- Added protect.js from plone4.csrffixes. This adds an X-CSRF-TOKEN header to ajax requests. Fixes https://github.com/plone/plone.protect/issues/42 [maurits]
- Use zope.interface decorator. [gforcada]
- Fixed AttributeError when calling safeWrite on a TestRequest, because this has no environ.. [maurits]
- Internationalized button in confirm.pt. [vincentfretin]
- Make sure transforms don’t fail on redirects. [lgraf]
- make sure to always compare content type with a string when checking if we should show the confirm-action view. [vangheem]
- Internationalized confirm.pt [vincentfretin]
- Disable editable border for @@confirm-action view. [lgraf]
- Make title and description show up on @@confirm-action view. [lgraf]
- Allow views to override ‘X-Frame-Options’ by setting the response header manually. [alecm]
- Avoid parsing redirect responses (this avoids a warning on the log files). [gforcada]
- Handle TypeError caused by getToolByName on an invalid context [vangheem]
- You can opt out of clickjacking protection by setting the environment variable PLONE_X_FRAME_OPTIONS to an empty string. [maurits]
- Be more flexible in parsing the PLONE_CSRF_DISABLED environment variable. We are no longer case sensitive, and we accept true, t, yes, y, 1 as true values. [maurits]
- Avoid TypeError when checking the content-type header. [maurits]
- Always force html serializer as the XHTML variant seems to cause character encoding issues [vangheem]
- Do not check writes to temporary storage like session storage [davisagli]
- make imports backward compatible [vangheem]
- patch pluggable auth with marmoset patch because the patch would not apply otherwise depending on somewhat-random import order [vangheem]
- get auto-csrf protection working on the zope root [vangheem]
- conditionally patch Products.PluggableAuthService if needed [vangheem]
- Do not raise ComponentLookupError on transform [vangheem]
- Fix pluggable auth CSRF warnings on zope root. Very difficult to reproduce. Just let plone.protect do it’s job also on zope root. [vangheem]
- Just return if the request object is not valid. [vangheem]
- fix pluggable auth CSRF warnings [vangheem]
- fix detecting safe object writes on non-GET requests [vangheem]
- instead of using _v_safe_write users should now use the safeWrite function in plone.protect.auto [vangheem]
- patch locking functions to use _v_safe_write attribute [vangheem]
- Be able to use _v_safe_write attribute to specify objects are safe to write [vangheem]
- handle zope root not having IKeyManager Utility and CRSF protection not being supported on zope root requests yet [vangheem]
- Add ITransform.transformBytes for protect transform to fix compatibility with plone.app.blocks’ ESI-rendering [atsoukka]
- auto CSRF protection: check for changes on all the storages [mamico]
- CSRF test fixed [mamico]
- auto-rotate keyrings [vangheem]
- use specific keyring for protected forms [vangheem]
- add automatic clickjacking protection(thanks to Manish Bhattacharya) [vangheem]
- add automatic CSRF protection [vangheem]
- Use constant time comparison to verify the authenticator. This is part of the fix for https://plone.org/products/plone/security/advisories/20121106/23 [davisagli]
- Add MANIFEST.in. [WouterVH]
- Add ability to customize the token created. [vangheem]
2.0 - 2010-07-18
- Update license to BSD following board decision. http://lists.plone.org/pipermail/membership/2009-August/001038.html [elro]
2.0a1 - 2009-11-14
- Removed deprecated AuthenticateForm class and zope.deprecation dependency. [hannosch]
- Avoid deprecation warning for the sha module in Python 2.6. [hannosch]
- Specify package dependencies [hannosch]
1.1 - 2008-06-02
- Add an optional GenericSetup profile to make it easier to install plone.protect. [mj]
1.0 - 2008-04-19
- The protect decorator had a serious design flaw which broke it. Added proper tests for it and fixed the problems. [wichert]
1.0rc1 - 2008-03-28
- Rename plone.app.protect to plone.protect: there is nothing Plone-specific about the functionality in this package and it really should be used outside of Plone as well. [wichert]
- Made utils.protect work with Zope >= 2.11. [stefan]
1.0b1 - March 7, 2008
- Refactor the code to offer a generic protect decorator for methods which takes a list of checkers as options. Add checkers for both the authenticator verification and HTTP POST-only. [wichert]
1.0a1 - January 27, 2008
- Initial release [wichert]
Release history Release notifications
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
|Filename, size & hash SHA256 hash help||File type||Python version||Upload date|
|plone.protect-4.1.2-py2.py3-none-any.whl (31.2 kB) Copy SHA256 hash SHA256||Wheel||py2.py3|
|plone.protect-4.1.2.tar.gz (28.5 kB) Copy SHA256 hash SHA256||Source||None|