Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 135 lines (110 sloc) 5.79 kb
33a035b @mitsuhiko Added security document
authored
1 Security Considerations
2 =======================
3
4 Web applications usually face all kinds of security problems and it's very
5 hard to get everything right. Flask tries to solve a few of these things
6 for you, but there are a couple more you have to take care of yourself.
7
8 Cross-Site Scripting (XSS)
9 --------------------------
10
11 Flask configures Jinja2 to automatically escape all values unless
12 explicitly told otherwise. This should rule out all XSS problems caused
13 in templates, but there are still other places where you have to be
14 careful:
15
16 - generating HTML without the help of Jinja2
17 - calling :class:`~flask.Markup` on data submitted by users
18 - sending out HTML from uploaded files, never do that, use the
19 `Content-Disposition: attachment` header to prevent that problem.
20 - sending out textfiles from uploaded files. Some browsers are using
21 content-type guessing based on the first few bytes so users could
22 trick a browser to execute HTML.
23
24 Cross-Site Request Forgery (CSRF)
25 ---------------------------------
26
27 Another big problem is CSRF. This is a very complex topic and I won't
28 outline it here in detail just mention what it is and how to theoretically
29 prevent it.
30
31 So if your authentication information is stored in cookies you have
32 implicit state management. By that I mean that the state of "being logged
33 in" is controlled by a cookie and that cookie is sent with each request to
34 a page. Unfortunately that really means "each request" so also requests
35 triggered by 3rd party sites. If you don't keep that in mind some people
36 might be able to trick your application's users with social engineering to
37 do stupid things without them knowing.
38
39 Say you have a specific URL that, when you sent `POST` requests to will
40 delete a user's profile (say `http://example.com/user/delete`). If an
41 attacker now creates a page that sents a post request to that page with
42 some JavaScript he just has to trick some users to that page and their
43 profiles will end up being deleted.
44
45 Imagine you would run Facebook with millions of concurrent users and
46 someone would send out links to images of little kittens. When a user
47 would go to that page their profiles would get deleted while they are
48 looking at images of fluffy cats.
49
50 So how can you prevent yourself from that? Basically for each request
51 that modifies content on the server you would have to either use a
52 one-time token and store that in the cookie **and** also transmit it with
53 the form data. After recieving the data on the server again you would
54 then have to compare the two tokens and ensure they are equal.
55
56 Why does not Flask do that for you? The ideal place for this to happen is
57 the form validation framework which does not exist in Flask.
58
59 .. _json-security:
60
61 JSON Security
62 -------------
63
64 JSON itself is a high-level serilization format, so there is barely
65 anything that could cause security problems, right? You can't declare
66 recursive structures that could cause problems and the only thing that
67 could possibly break are very large responses that can cause some kind of
68 denial of service at the receivers side.
69
52f38bb @mitsuhiko Fixed a broken sentence
authored
70 However there is a catch. Due to how browsers work the CSRF issue comes
71 up with JSON unfortunately. Fortunately there is also a weird part of the
33a035b @mitsuhiko Added security document
authored
72 JavaScript specification that can be used to solve that problem easily and
73 Flask is kinda doing that for you by preventing you from doing dangerous
74 stuff. Unfortunately that protection is only there for
75 :func:`~flask.jsonify` so you are still at risk when using other ways to
76 generate JSON.
77
78 So what is the issue and how to avoid it? The problem are arrays at
79 toplevel in JSON. Imagine you send the following data out in a JSON
80 request. Say that's exporting the names and email adresses of all your
81 friends for a part of the userinterface that is written in JavaScript.
82 Not very uncommon:
83
84 .. sourcecode:: javascript
85
86 [
87 {"username": "admin",
88 "email": "admin@localhost"}
89 ]
90
91 And it is doing that of course only as long as you are logged in and only
92 for you. And it is doing that for all `GET` requests to a certain URL,
93 say the URL for that request is
94 ``http://example.com/api/get_friends.json``.
95
96 So now what happens if a clever hacker is embedding this to his website
97 and social engineers a victim to visiting his site:
98
99 .. sourcecode:: html
100
101 <script type=text/javascript>
102 var captured = [];
103 var oldArray = Array;
104 function Array() {
105 var obj = this, id = 0, capture = function(value) {
106 obj.__defineSetter__(id++, capture);
107 if (value)
108 captured.push(value);
109 };
110 capture();
111 }
112 </script>
113 <script type=text/javascript
114 src=http://example.com/api/get_friends.json></script>
115 <script type=text/javascript>
116 Array = oldArray;
117 // now we have all the data in the captured array.
118 </script>
119
120 If you know a bit of JavaScript internals you might know that it's
121 possible to patch constructors and register callbacks for setters. An
122 attacker can use this (like above) to get all the data you exported in
123 your JSON file. The browser will totally ignore the ``application/json``
124 mimetype if ``text/javascript`` is defined as content type in the script
125 tag and evaluate that as JavaScript. Because toplevel array elements are
126 allowed (albeit useless) and we hooked in our own constructor, after that
127 page loaded the data from the JSON response is in the `captured` array.
128
129 Because it is a syntax error in JavaScript to have an object literal
130 (``{...}``) toplevel an attacker could not just do a request to an
131 external URL with the script tag to load up the data. So what Flask does
132 is only allowing objects as toplevel elements when using
133 :func:`~flask.jsonify`. Make sure to do the same when using an ordinary
134 JSON generate function.
Something went wrong with that request. Please try again.