Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1)

Side by Side Diff: content/common/content_security_policy/csp_policy.cc

Issue 2612793002: Implement ContentSecurityPolicy on the browser-side. (Closed)
Patch Set: Temporary re-add the parser + transmit parsed CSP over IPC. Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <sstream>
6
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "content/common/content_security_policy/csp_context.h"
10
11 namespace content {
12
13 namespace {
14
15 static CSPDirective::Name CSPFallback(CSPDirective::Name directive) {
16 if (directive == CSPDirective::FrameSrc)
17 return CSPDirective::ChildSrc;
18 if (directive == CSPDirective::ChildSrc)
19 return CSPDirective::DefaultSrc;
20 return CSPDirective::Unknown;
21 }
22
23 void ReportUnsupportedDirective(CSPContext* context,
24 const base::StringPiece& name) {
25 std::string message = "Unrecognized Content-Security-Policy directive '" +
26 name.as_string() + "'.\n";
27 context->LogToConsole(message);
28 }
29
30 void ReportDuplicateDirective(CSPContext* context,
31 const base::StringPiece& directive) {
32 std::string message =
33 "Ignoring duplicate Content-Security-Policy directive '" +
34 directive.as_string() + "'.\n";
35 context->LogToConsole(message);
36 }
37
38 void ReportInvalidDirectiveInMeta(CSPContext* context,
39 const base::StringPiece& directive) {
40 std::string message =
41 "Content Security Policies delivered via a <meta> element may not "
42 "contain the " +
43 directive.as_string() + " directive.";
44 context->LogToConsole(message);
45 }
46
47 void AddReportURI(CSPPolicy* policy,
48 CSPContext* context,
49 const base::StringPiece& directive_value) {
50 if (!policy->report_end_points.empty())
51 ReportDuplicateDirective(context, "report-uri");
52 // Remove report-uri in meta policies, per
53 // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
54 if (policy->source == blink::WebContentSecurityPolicySourceMeta) {
55 ReportInvalidDirectiveInMeta(context, "report-uri");
56 return;
57 }
58
59 for (const base::StringPiece& end_point :
60 base::SplitStringPiece(directive_value, " ", base::TRIM_WHITESPACE,
61 base::SPLIT_WANT_NONEMPTY)) {
62 policy->report_end_points.push_back(end_point.as_string());
63 }
64 }
65
66 void AddDirective(CSPPolicy* policy,
67 CSPContext* context,
68 const base::StringPiece& directive_name,
69 const base::StringPiece& directive_value) {
70 CSPDirective::Name name =
71 CSPDirective::StringToName(directive_name.as_string());
72
73 // Unknown directive
74 if (name == CSPDirective::Unknown) {
75 ReportUnsupportedDirective(context, directive_name);
76 }
77
78 // Report-uri
79 if (name == CSPDirective::ReportURI) {
80 AddReportURI(policy, context, directive_value);
81 return;
82 }
83
84 // Remove frame-ancestors directives in meta policies, per
85 // https://www.w3.org/TR/CSP2/#delivery-html-meta-element.
86 if (name == CSPDirective::FrameAncestors &&
87 policy->source == blink::WebContentSecurityPolicySourceMeta) {
88 ReportInvalidDirectiveInMeta(context, directive_name);
89 return;
90 }
91
92 for (const auto& directive : policy->directives) {
93 if (directive.name == name)
94 ReportDuplicateDirective(context, directive_name);
95 }
96
97 policy->directives.emplace_back(
98 name, CSPSourceList::Parse(context, directive_name, directive_value));
99 }
100
101 } // namespace
102
103 CSPPolicy::CSPPolicy()
104 : disposition(blink::WebContentSecurityPolicyTypeEnforce),
105 source(blink::WebContentSecurityPolicySourceHTTP),
106 directives(),
107 report_end_points() {}
108
109 CSPPolicy::CSPPolicy(blink::WebContentSecurityPolicyType disposition,
110 blink::WebContentSecurityPolicySource source,
111 const std::vector<CSPDirective>& directives,
112 const std::vector<std::string>& report_end_points)
113 : disposition(disposition),
114 source(source),
115 directives(directives),
116 report_end_points(report_end_points) {}
117
118 CSPPolicy::CSPPolicy(const CSPPolicy&) = default;
119 CSPPolicy::~CSPPolicy() = default;
120
121 // static
122 CSPPolicy CSPPolicy::Parse(CSPContext* context,
123 const ContentSecurityPolicyHeader& header) {
124 CSPPolicy policy;
125 policy.disposition = header.type;
126 policy.source = header.source;
127
128 // Split by ";"
129 for (const base::StringPiece& serialized_directive :
130 base::SplitStringPiece(header.header_value, ";", base::TRIM_WHITESPACE,
131 base::SPLIT_WANT_NONEMPTY)) {
132 base::StringPiece directive_name;
133 base::StringPiece directive_value;
134
135 // Split serialized-directive into { directive-name , directive-value }.
136 size_t first_space_pos =
137 serialized_directive.find_first_of(base::kWhitespaceASCII, 0);
138 directive_name = serialized_directive.substr(0, first_space_pos);
139 if (first_space_pos != std::string::npos) {
140 size_t first_non_space_pos = serialized_directive.find_first_not_of(
141 base::kWhitespaceASCII, first_space_pos);
142 directive_value =
143 serialized_directive.substr(first_non_space_pos, std::string::npos);
144 }
145
146 AddDirective(&policy, context, directive_name, directive_value);
147 }
148 return policy;
149 }
150
151 bool CSPPolicy::Allow(CSPContext* context,
152 CSPDirective::Name directive_name,
153 const GURL& url,
154 bool is_redirect) const {
155 CSPDirective::Name current_directive_name = directive_name;
156 do {
157 // Search for the matching directive.
158 for (const CSPDirective& directive : directives) {
159 if (directive.name == current_directive_name) {
160 return AllowDirective(context, directive_name, directive, url,
161 is_redirect);
162 }
163 }
164
165 current_directive_name = CSPFallback(current_directive_name);
166 } while (current_directive_name != CSPDirective::Unknown);
167 return false;
168 }
169
170 std::string CSPPolicy::ToString() const {
171 std::stringstream text;
172 bool is_first_policy = true;
173 for (const CSPDirective& directive : directives) {
174 if (!is_first_policy)
175 text << "; ";
176 text << directive.ToString();
177 is_first_policy = false;
178 }
179 return text.str();
180 }
181
182 bool CSPPolicy::AllowDirective(CSPContext* context,
183 CSPDirective::Name directive_name,
184 const CSPDirective& directive,
185 const GURL& url,
186 bool is_redirect) const {
187 if (directive.source_list.Allow(context, url, is_redirect))
188 return true;
189
190 ReportViolation(context, directive_name, directive, url);
191
192 return disposition == blink::WebContentSecurityPolicyTypeReport;
193 }
194
195 void CSPPolicy::ReportViolation(CSPContext* context,
196 const CSPDirective::Name directive_name,
197 const CSPDirective& directive,
198 const GURL& url) const {
199 // We should never have a violation against `child-src` or `default-src`
200 // directly; the effective directive should always be one of the explicit
201 // fetch directives.
202 DCHECK_NE(directive_name, CSPDirective::DefaultSrc);
203 DCHECK_NE(directive_name, CSPDirective::ChildSrc);
204
205 std::stringstream message;
206
207 if (disposition == blink::WebContentSecurityPolicyTypeReport)
208 message << "[Report Only] ";
209
210 if (directive_name == CSPDirective::FormAction)
211 message << "Refused to send form data to '";
212 else if (directive_name == CSPDirective::FrameSrc)
213 message << "Refused to frame '";
214 else if (directive_name == CSPDirective::FrameAncestors)
215 message << "Refused to display '";
216
217 // TODO(arthursonzogni): Elide url.
218 message << url
219 << "' because it violates the following Content Security Policy "
220 "directive: \""
221 // TODO(arthursonzogni): Pass the full CSPDirective.
222 << directive.ToString() << "\"";
223
224 if (directive.name != directive_name)
225 message << " Note that '" << CSPDirective::NameToString(directive_name)
226 << "' was not explicitly set, so '"
227 << CSPDirective::NameToString(directive.name)
228 << "' is used as a fallback.";
229
230 message << "\n";
231
232 context->LogToConsole(message.str());
233 context->ReportViolation(CSPDirective::NameToString(directive.name),
234 CSPDirective::NameToString(directive_name),
235 message.str(), url, report_end_points,
236 "", // TODO(arthursonzogni): Pass header.
237 disposition);
238 }
239
240 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698