Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3""" 

4camcops_server/cc_modules/cc_response.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27**Implements a Pyramid Response object customized for CamCOPS.** 

28 

29""" 

30 

31from typing import TYPE_CHECKING 

32 

33from pyramid.response import Response 

34 

35from camcops_server.cc_modules.cc_baseconstants import ( 

36 DEFORM_SUPPORTS_CSP_NONCE, 

37) 

38 

39if TYPE_CHECKING: 

40 from camcops_server.cc_modules.cc_request import CamcopsRequest 

41 

42 

43class CamcopsResponse(Response): 

44 """ 

45 Response class, inheriting from Pyramid's response. 

46 

47 We do this mainly to set the HTTP ``Content-Security-Policy`` header to 

48 match the nonce set by the 

49 :class:``camcops_server.cc_modules.cc_request.CamcopsRequest``. 

50 

51 However, once this class exists, it may as well set all the standard 

52 headers, rather than using additional middleware. 

53 """ 

54 def __init__(self, camcops_request: "CamcopsRequest", **kwargs) -> None: 

55 super().__init__(**kwargs) 

56 nonce = camcops_request.nonce 

57 self.headers.update([ 

58 # List of key, value tuples: 

59 

60 # ----------------------------------------------------------------- 

61 # Cache-Control: Caching 

62 # ----------------------------------------------------------------- 

63 # NOT THIS: 

64 # ("Cache-Control", "no-cache, no-store, must-revalidate"), 

65 # ... or we get a ZAP error "Incomplete or No Cache-control and 

66 # Pragma HTTP Header Set" 

67 # ... note that Pragma is HTTP/1.0 and cache-control is HTTP/1.1, 

68 # so you don't have to do both. 

69 # BUT that prevents caching of images (e.g. logos), and we don't 

70 # want that. 

71 # 

72 # The Pyramid @view_config decorator (or add_view function) takes 

73 # an "http_cache" parameter, as per 

74 # https://pyramid-pt-br.readthedocs.io/en/latest/api/config.html#pyramid.config.Configurator.add_view # noqa 

75 # and it looks like viewderivers.py implements this. The default 

76 # does not set the HTTP "cache-control" header, explained at 

77 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control # noqa 

78 # The basic options are: 

79 # - 0: do not cache 

80 # - integer_seconds, or datetime.timedelta: lifespan 

81 # - tuple (lifespan, dictionary_of_extra_cache_control_details) 

82 # - tuple (None, dictionary_of_extra_cache_control_details) 

83 # We now set http_cache for all our views via view_config, as well 

84 # as the equivalent of using cache_max_age for add_static_view(). 

85 # 

86 # However, this (as an additional Cache-Control header -- as well 

87 # as any "cache, it's static" or "don't cache" header) sorts out 

88 # any ZAP complaints: 

89 

90 ("Cache-Control", 'no-cache="Set-Cookie, Set-Cookie2"'), 

91 

92 # ----------------------------------------------------------------- 

93 # Content-Security-Policy: Control resources that are permitted to 

94 # load, to mitigate against cross-site scripting attacks 

95 # ----------------------------------------------------------------- 

96 # - Content-Security-Policy. 

97 # - Recommended by Falanx penetration testing. 

98 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy # noqa 

99 # - Defaults from https://owasp.org/www-project-secure-headers/ 

100 # - Re scripts: see https://csper.io/blog/no-more-unsafe-inline 

101 # - Re nonces (and in general): see 

102 # https://stackoverflow.com/questions/42922784/what-s-the-purpose-of-the-html-nonce-attribute-for-script-and-style-elements # noqa 

103 # - Note that multiple CSP headers combine to produce the most 

104 # restrictive and can get confusing; best to use one. See 

105 # https://chrisguitarguy.com/2019/07/05/working-with-multiple-content-security-policy-headers/ # noqa 

106 ( 

107 "Content-Security-Policy", 

108 # A single string: 

109 ( 

110 # The secure policy: 

111 

112 "default-src 'self' data:; " 

113 "object-src 'none'; " 

114 "child-src 'self'; " 

115 

116 f"style-src 'nonce-{nonce}' 'self'; " 

117 # ... meaning: allow inline CSS only if it is tagged with 

118 # this nonce, via <style nonce="XXX">, or if it comes from 

119 # our site ('self'). See 

120 # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src # noqa 

121 

122 # And similarly for scripts: 

123 f"script-src 'nonce-{nonce}' 'self'; " 

124 # ... "unsafe-eval" is currently required by deform.js, in 

125 # addSequenceItem(). Deform stores prototype code and then 

126 # clones it when you add a sequence item; this involves 

127 # evaluation. 

128 

129 "frame-ancestors 'none'; " 

130 "upgrade-insecure-requests; " 

131 "block-all-mixed-content" 

132 ) if DEFORM_SUPPORTS_CSP_NONCE else ( 

133 # The less secure policy, for Deform: 

134 

135 "default-src 'self' data:; " 

136 "object-src 'none'; " 

137 "child-src 'self'; " 

138 

139 "style-src 'self' 'unsafe-inline'; " 

140 "script-src 'self' 'unsafe-inline' 'unsafe-eval'; " 

141 

142 "frame-ancestors 'none'; " 

143 "upgrade-insecure-requests; " 

144 "block-all-mixed-content" 

145 ) 

146 ), 

147 

148 # ----------------------------------------------------------------- 

149 # Strict-Transport-Security: Enforce HTTPS through the client. 

150 # ----------------------------------------------------------------- 

151 # - In part this is by e.g. telling Google (and thus Chrome) that 

152 # your site always uses HTTPS, to prevent HTTP-based spoofing. 

153 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security # noqa 

154 # - Advice is at 

155 # https://blog.qualys.com/vulnerabilities-research/2016/03/28/the-importance-of-a-proper-http-strict-transport-security-implementation-on-your-web-server # noqa 

156 

157 ("Strict-Transport-Security", "max-age=31536000"), # = 1 year 

158 

159 # ----------------------------------------------------------------- 

160 # X-Content-Type-Options: Opt out of MIME type sniffing 

161 # ----------------------------------------------------------------- 

162 # - Recommended by ZAP penetration testing. 

163 # ... otherwise, the error is "X-Content-Type-Options Header 

164 # Missing" 

165 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options # noqa 

166 

167 ("X-Content-Type-Options", "nosniff"), 

168 

169 # ----------------------------------------------------------------- 

170 # X-Frame-Options: Prevent rendering within a frame 

171 # ----------------------------------------------------------------- 

172 # - Recommended by ZAP penetration testing. 

173 # ... otherwise, the error is "X-Frame-Options Header Not Set" 

174 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options # noqa 

175 

176 ("X-Frame-Options", "DENY"), 

177 

178 # ----------------------------------------------------------------- 

179 # X-XSS-Protection: Check for cross-site scripting attacks 

180 # ----------------------------------------------------------------- 

181 # - Recommended by Falanx penetration testing. 

182 # - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection # noqa 

183 

184 ("X-XSS-Protection", "1"), 

185 

186 ]) 

187 

188 

189def camcops_response_factory(request: "CamcopsRequest") -> Response: 

190 """ 

191 Factory function to make a response object. 

192 """ 

193 return CamcopsResponse(camcops_request=request)