You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
"description": "How to protect Plone against CSRF attacks."
5
+
"property=og:description": "How to protect Plone against CSRF attacks."
6
+
"property=og:title": "Cross-Site Request Forgery protection in Plone"
7
+
"keywords": "CSRF, security, token, protection"
8
8
---
9
9
10
10
(classic-ui-csrf-label)=
11
11
12
12
# Cross-Site Request Forgery (CSRF)
13
13
14
14
Cross-Site Request Forgery (CSRF or XSRF) is a type of web attack that allows an attacker to send malicious requests to a web application on behalf of a legitimate user.
15
-
The attack works by tricking the user's web browser into sending a request to the web application that the user did not intentionally make. This can allow an attacker to perform actions on the web application without the user's knowledge or consent.
15
+
The attack works by tricking the user's web browser into sending a request to the web application that the user did not intentionally make.
16
+
This can allow an attacker to perform actions on the web application without the user's knowledge or consent.
16
17
17
18
For example, consider a web application that allows users to transfer money between accounts.
18
19
An attacker could craft a malicious link or form that, when clicked or submitted by a victim, would transfer money from the victim's account to the attacker's account.
@@ -38,16 +39,141 @@ This includes, but is not limited to the following:
38
39
39
40
## Manual protection
40
41
41
-
TODO
42
-
- protecting views
43
-
- POST Only
44
-
- adding a token to an URL for a link (in view code, in template code)
42
+
To ensure that code that is not part of a database transaction, such as code that writes to an external API or a service that is not automatically included in the transaction mechanism, is protected, you will need to manually implement protection for that code.
45
43
46
-
## Allowing writes in absence of a protecting token
44
+
`plone.protect` offers the `@protect` decorator.
45
+
The decorator expects a callable to perform the check.
46
+
There are two checks implemented in `plone.protect`:
47
47
48
-
TODO
49
-
- marking the request to allow all writes
50
-
- marking single modified objects explicit to allow them to persist
48
+
### CSRF token check with `CheckAuthenticator`
49
+
50
+
Checks whether a valid CSRF token is present in the request and raises `Unauthorized` if not.
51
+
52
+
Usage example:
53
+
54
+
```python
55
+
from plone.protect import CheckAuthenticator
56
+
from plone.protect import protec
57
+
58
+
@protect(CheckAuthenticator)
59
+
defwrite_to_api_or_service(self):
60
+
# code here
61
+
...
62
+
```
63
+
64
+
### HTTP POST check with `PostOnly`
65
+
66
+
Checks whether the request is an HTTP POST and raise `Unauthorized` if not.
67
+
This helps to mitigate clicks on malicious links.
68
+
69
+
Usage example:
70
+
71
+
```python
72
+
from plone.protect import PostOnly
73
+
from plone.protect import protect
74
+
75
+
@protect(PostOnly)
76
+
defwrite_to_api_or_service(self):
77
+
# code here
78
+
...
79
+
```
80
+
81
+
## How to add a CSRF-Token to a Link or Form
82
+
83
+
To pass a token you need either to:
84
+
85
+
- pass an HTTP GET parameter name `_authenticator` with the token as the value,
86
+
- include a form field named `_authenticator` with the token as the value and submit it with the form, or
87
+
- add an HTTP header named `X-CSRF-TOKEN` with the token as the value.
88
+
89
+
To add a token as an HTTP GET parameter to a link in a template, you can utilize the authenticator view:
# Reassemble the URL with the updated query string
130
+
final_url = urlunparse(
131
+
(
132
+
parsed_url.scheme,
133
+
parsed_url.netloc,
134
+
parsed_url.path,
135
+
parsed_url.params,
136
+
new_query,
137
+
parsed_url.fragment)
138
+
)
139
+
```
140
+
141
+
142
+
## How to allow writes in absence of a protecting token
143
+
144
+
To allow certain objects to be modified and written to the database without protection, follow these steps:
145
+
146
+
1. Identify the modified object as a single object in the database.
147
+
2. If an attribute of the object is a "persistent" attribute (e.g., a PersistentDict or PersistentList instance, a BTree, or an annotation), use this instead.
148
+
3. Use the `safeWrite` function to mark the object as safe for writing.
149
+
150
+
Note: This is the preferred method for allowing modification and writing of specific objects to the database.
151
+
152
+
```python
153
+
from plone.protect.utils import safeWrite
154
+
155
+
defsome_fucntion(obj, request):
156
+
safeWrite(obj, request)
157
+
obj.foo ="bar"# modify obj
158
+
```
159
+
160
+
If there are lots of modifications or it is not possible to identify the boundaries of the writes, the protection can be disabled for the whole current request.
161
+
Then the request can be marked with the `IDisableCSRFProtection` marker interface.
162
+
163
+
```python
164
+
from plone.protect.interfaces import IDisableCSRFProtection
165
+
from zope.interface import alsoProvides
166
+
167
+
defsome_function(request):
168
+
alsoProvides(request, IDisableCSRFProtection)
169
+
# modify the database here
170
+
```
171
+
172
+
Disabling all CSRF protection for the whole Plone instance is possible by starting Plone with the environment variable `PLONE_CSRF_DISABLED=true` set.
173
+
This is not recommended but can be handy temporarily in special situations.
174
+
175
+
176
+
## Further reading
51
177
52
178
```{seealso}
53
179
The [README file of `plone.protect`](https://github.com/plone/plone.protect/blob/master/README.rst) explains the usage and also validation in detail.
0 commit comments