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

Side by Side Diff: third_party/WebKit/Source/bindings/core/v8/BindingSecurity.cpp

Issue 2706923002: Rework security checks to be based on Window rather than Frame. (Closed)
Patch Set: Do not hardcode V8Window::wrapperTypeInfo Created 3 years, 7 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
1 /* 1 /*
2 * Copyright (C) 2009 Google Inc. All rights reserved. 2 * Copyright (C) 2009 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are 5 * modification, are permitted provided that the following conditions are
6 * met: 6 * met:
7 * 7 *
8 * * Redistributions of source code must retain the above copyright 8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer. 9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above 10 * * Redistributions in binary form must reproduce the above
(...skipping 15 matching lines...) Expand all
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */ 29 */
30 30
31 #include "bindings/core/v8/BindingSecurity.h" 31 #include "bindings/core/v8/BindingSecurity.h"
32 32
33 #include "bindings/core/v8/ExceptionState.h" 33 #include "bindings/core/v8/ExceptionState.h"
34 #include "bindings/core/v8/V8BindingForCore.h" 34 #include "bindings/core/v8/V8BindingForCore.h"
35 #include "bindings/core/v8/V8Location.h" 35 #include "bindings/core/v8/V8Location.h"
36 #include "bindings/core/v8/V8Window.h"
36 #include "core/dom/Document.h" 37 #include "core/dom/Document.h"
38 #include "core/frame/DOMWindow.h"
37 #include "core/frame/LocalDOMWindow.h" 39 #include "core/frame/LocalDOMWindow.h"
38 #include "core/frame/LocalFrame.h" 40 #include "core/frame/LocalFrame.h"
39 #include "core/frame/Location.h" 41 #include "core/frame/Location.h"
40 #include "core/frame/Settings.h" 42 #include "core/frame/Settings.h"
41 #include "core/html/HTMLFrameElementBase.h" 43 #include "core/html/HTMLFrameElementBase.h"
42 #include "core/workers/MainThreadWorkletGlobalScope.h" 44 #include "core/workers/MainThreadWorkletGlobalScope.h"
43 #include "platform/bindings/WrapperCreationSecurityCheck.h" 45 #include "platform/bindings/WrapperCreationSecurityCheck.h"
44 #include "platform/weborigin/SecurityOrigin.h" 46 #include "platform/weborigin/SecurityOrigin.h"
45 47
46 namespace blink { 48 namespace blink {
47 49
48 namespace { 50 namespace {
49 51
50 bool CanAccessFrameInternal(const LocalDOMWindow* accessing_window, 52 bool CanAccessWindowInternal(const LocalDOMWindow* accessing_window,
51 const SecurityOrigin* target_frame_origin, 53 const DOMWindow* target_window) {
52 const DOMWindow* target_window) {
53 SECURITY_CHECK(!(target_window && target_window->GetFrame()) || 54 SECURITY_CHECK(!(target_window && target_window->GetFrame()) ||
54 target_window == target_window->GetFrame()->DomWindow()); 55 target_window == target_window->GetFrame()->DomWindow());
55 56
56 // It's important to check that target_window is a LocalDOMWindow: it's 57 // It's important to check that target_window is a LocalDOMWindow: it's
57 // possible for a remote frame and local frame to have the same security 58 // possible for a remote frame and local frame to have the same security
58 // origin, depending on the model being used to allocate Frames between 59 // origin, depending on the model being used to allocate Frames between
59 // processes. See https://crbug.com/601629. 60 // processes. See https://crbug.com/601629.
60 if (!(accessing_window && target_window && target_window->IsLocalDOMWindow())) 61 if (!(accessing_window && target_window && target_window->IsLocalDOMWindow()))
61 return false; 62 return false;
62 63
63 const SecurityOrigin* accessing_origin = 64 const SecurityOrigin* accessing_origin =
64 accessing_window->document()->GetSecurityOrigin(); 65 accessing_window->document()->GetSecurityOrigin();
65 if (!accessing_origin->CanAccess(target_frame_origin)) 66 const LocalDOMWindow* local_target_window = ToLocalDOMWindow(target_window);
67 if (!accessing_origin->CanAccess(
68 local_target_window->document()->GetSecurityOrigin())) {
66 return false; 69 return false;
70 }
67 71
68 // Notify the loader's client if the initial document has been accessed. 72 // Notify the loader's client if the initial document has been accessed.
69 LocalFrame* target_frame = ToLocalDOMWindow(target_window)->GetFrame(); 73 LocalFrame* target_frame = local_target_window->GetFrame();
70 if (target_frame && 74 if (target_frame &&
71 target_frame->Loader().StateMachine()->IsDisplayingInitialEmptyDocument()) 75 target_frame->Loader().StateMachine()->IsDisplayingInitialEmptyDocument())
72 target_frame->Loader().DidAccessInitialDocument(); 76 target_frame->Loader().DidAccessInitialDocument();
73 77
74 return true; 78 return true;
75 } 79 }
76 80
77 bool CanAccessFrame(const LocalDOMWindow* accessing_window, 81 bool CanAccessWindow(const LocalDOMWindow* accessing_window,
78 const SecurityOrigin* target_frame_origin, 82 const DOMWindow* target_window,
79 const DOMWindow* target_window, 83 ExceptionState& exception_state) {
80 ExceptionState& exception_state) { 84 if (CanAccessWindowInternal(accessing_window, target_window))
81 if (CanAccessFrameInternal(accessing_window, target_frame_origin,
82 target_window))
83 return true; 85 return true;
84 86
85 if (target_window) 87 if (target_window)
86 exception_state.ThrowSecurityError( 88 exception_state.ThrowSecurityError(
87 target_window->SanitizedCrossDomainAccessErrorMessage(accessing_window), 89 target_window->SanitizedCrossDomainAccessErrorMessage(accessing_window),
88 target_window->CrossDomainAccessErrorMessage(accessing_window)); 90 target_window->CrossDomainAccessErrorMessage(accessing_window));
89 return false; 91 return false;
90 } 92 }
91 93
92 bool CanAccessFrame(const LocalDOMWindow* accessing_window, 94 bool CanAccessWindow(const LocalDOMWindow* accessing_window,
93 SecurityOrigin* target_frame_origin, 95 const DOMWindow* target_window,
94 const DOMWindow* target_window, 96 BindingSecurity::ErrorReportOption reporting_option) {
95 BindingSecurity::ErrorReportOption reporting_option) { 97 if (CanAccessWindowInternal(accessing_window, target_window))
96 if (CanAccessFrameInternal(accessing_window, target_frame_origin,
97 target_window))
98 return true; 98 return true;
99 99
100 if (accessing_window && target_window && 100 if (accessing_window && target_window &&
101 reporting_option == BindingSecurity::ErrorReportOption::kReport) 101 reporting_option == BindingSecurity::ErrorReportOption::kReport)
102 accessing_window->PrintErrorMessage( 102 accessing_window->PrintErrorMessage(
103 target_window->CrossDomainAccessErrorMessage(accessing_window)); 103 target_window->CrossDomainAccessErrorMessage(accessing_window));
104 return false; 104 return false;
105 } 105 }
106 106
107 DOMWindow* FindWindow(v8::Isolate* isolate,
108 const WrapperTypeInfo* type,
109 v8::Local<v8::Object> holder) {
110 if (V8Window::wrapperTypeInfo.Equals(type))
111 return V8Window::toImpl(holder);
112
113 if (V8Location::wrapperTypeInfo.Equals(type))
114 return V8Location::toImpl(holder)->DomWindow();
115
116 // This function can handle only those types listed above.
117 NOTREACHED();
118 return nullptr;
119 }
120
107 } // namespace 121 } // namespace
108 122
109 bool BindingSecurity::ShouldAllowAccessTo( 123 bool BindingSecurity::ShouldAllowAccessTo(
110 const LocalDOMWindow* accessing_window, 124 const LocalDOMWindow* accessing_window,
111 const DOMWindow* target, 125 const DOMWindow* target,
112 ExceptionState& exception_state) { 126 ExceptionState& exception_state) {
113 DCHECK(target); 127 DCHECK(target);
114 const Frame* frame = target->GetFrame(); 128
115 if (!frame || !frame->GetSecurityContext()) 129 // TODO(https://crbug.com/723057): This is intended to match the legacy
130 // behavior of when access checks revolved around Frame pointers rather than
131 // DOMWindow pointers. This prevents web-visible behavior changes, since the
132 // previous implementation had to follow the back pointer to the Frame, and
133 // would have to early return when it was null.
134 if (!target->GetFrame())
116 return false; 135 return false;
117 return CanAccessFrame(accessing_window, 136
118 frame->GetSecurityContext()->GetSecurityOrigin(), 137 return CanAccessWindow(accessing_window, target, exception_state);
119 target, exception_state);
120 } 138 }
121 139
122 bool BindingSecurity::ShouldAllowAccessTo( 140 bool BindingSecurity::ShouldAllowAccessTo(
123 const LocalDOMWindow* accessing_window, 141 const LocalDOMWindow* accessing_window,
124 const DOMWindow* target, 142 const DOMWindow* target,
125 ErrorReportOption reporting_option) { 143 ErrorReportOption reporting_option) {
126 DCHECK(target); 144 DCHECK(target);
127 const Frame* frame = target->GetFrame(); 145
128 if (!frame || !frame->GetSecurityContext()) 146 // TODO(https://crbug.com/723057): This is intended to match the legacy
147 // behavior of when access checks revolved around Frame pointers rather than
148 // DOMWindow pointers. This prevents web-visible behavior changes, since the
149 // previous implementation had to follow the back pointer to the Frame, and
150 // would have to early return when it was null.
151 if (!target->GetFrame())
129 return false; 152 return false;
130 return CanAccessFrame(accessing_window, 153
131 frame->GetSecurityContext()->GetSecurityOrigin(), 154 return CanAccessWindow(accessing_window, target, reporting_option);
132 target, reporting_option);
133 } 155 }
134 156
135 bool BindingSecurity::ShouldAllowAccessTo( 157 bool BindingSecurity::ShouldAllowAccessTo(
136 const LocalDOMWindow* accessing_window,
137 const EventTarget* target,
138 ExceptionState& exception_state) {
139 DCHECK(target);
140 const DOMWindow* window = target->ToDOMWindow();
141 if (!window) {
142 // We only need to check the access to Window objects which are
143 // cross-origin accessible. If it's not a Window, the object's
144 // origin must always be the same origin (or it already leaked).
145 return true;
146 }
147 const Frame* frame = window->GetFrame();
148 if (!frame || !frame->GetSecurityContext())
149 return false;
150 return CanAccessFrame(accessing_window,
151 frame->GetSecurityContext()->GetSecurityOrigin(),
152 window, exception_state);
153 }
154
155 bool BindingSecurity::ShouldAllowAccessTo(
156 const LocalDOMWindow* accessing_window, 158 const LocalDOMWindow* accessing_window,
157 const Location* target, 159 const Location* target,
158 ExceptionState& exception_state) { 160 ExceptionState& exception_state) {
159 DCHECK(target); 161 DCHECK(target);
160 const Frame* frame = target->GetFrame(); 162
161 if (!frame || !frame->GetSecurityContext()) 163 // TODO(https://crbug.com/723057): This is intended to match the legacy
164 // behavior of when access checks revolved around Frame pointers rather than
165 // DOMWindow pointers. This prevents web-visible behavior changes, since the
166 // previous implementation had to follow the back pointer to the Frame, and
167 // would have to early return when it was null.
168 if (!target->DomWindow()->GetFrame())
162 return false; 169 return false;
163 return CanAccessFrame(accessing_window, 170
164 frame->GetSecurityContext()->GetSecurityOrigin(), 171 return CanAccessWindow(accessing_window, target->DomWindow(),
165 frame->DomWindow(), exception_state); 172 exception_state);
166 } 173 }
167 174
168 bool BindingSecurity::ShouldAllowAccessTo( 175 bool BindingSecurity::ShouldAllowAccessTo(
169 const LocalDOMWindow* accessing_window, 176 const LocalDOMWindow* accessing_window,
170 const Location* target, 177 const Location* target,
171 ErrorReportOption reporting_option) { 178 ErrorReportOption reporting_option) {
172 DCHECK(target); 179 DCHECK(target);
173 const Frame* frame = target->GetFrame(); 180
174 if (!frame || !frame->GetSecurityContext()) 181 // TODO(https://crbug.com/723057): This is intended to match the legacy
182 // behavior of when access checks revolved around Frame pointers rather than
183 // DOMWindow pointers. This prevents web-visible behavior changes, since the
184 // previous implementation had to follow the back pointer to the Frame, and
185 // would have to early return when it was null.
186 if (!target->DomWindow()->GetFrame())
175 return false; 187 return false;
176 return CanAccessFrame(accessing_window, 188
177 frame->GetSecurityContext()->GetSecurityOrigin(), 189 return CanAccessWindow(accessing_window, target->DomWindow(),
178 frame->DomWindow(), reporting_option); 190 reporting_option);
179 } 191 }
180 192
181 bool BindingSecurity::ShouldAllowAccessTo( 193 bool BindingSecurity::ShouldAllowAccessTo(
182 const LocalDOMWindow* accessing_window, 194 const LocalDOMWindow* accessing_window,
183 const Node* target, 195 const Node* target,
184 ExceptionState& exception_state) { 196 ExceptionState& exception_state) {
185 if (!target) 197 if (!target)
186 return false; 198 return false;
187 return CanAccessFrame(accessing_window, 199 return CanAccessWindow(accessing_window, target->GetDocument().domWindow(),
188 target->GetDocument().GetSecurityOrigin(), 200 exception_state);
189 target->GetDocument().domWindow(), exception_state);
190 } 201 }
191 202
192 bool BindingSecurity::ShouldAllowAccessTo( 203 bool BindingSecurity::ShouldAllowAccessTo(
193 const LocalDOMWindow* accessing_window, 204 const LocalDOMWindow* accessing_window,
194 const Node* target, 205 const Node* target,
195 ErrorReportOption reporting_option) { 206 ErrorReportOption reporting_option) {
196 if (!target) 207 if (!target)
197 return false; 208 return false;
198 return CanAccessFrame(accessing_window, 209 return CanAccessWindow(accessing_window, target->GetDocument().domWindow(),
199 target->GetDocument().GetSecurityOrigin(), 210 reporting_option);
200 target->GetDocument().domWindow(), reporting_option);
201 } 211 }
202 212
203 bool BindingSecurity::ShouldAllowAccessToFrame( 213 bool BindingSecurity::ShouldAllowAccessToFrame(
204 const LocalDOMWindow* accessing_window, 214 const LocalDOMWindow* accessing_window,
205 const Frame* target, 215 const Frame* target,
206 ExceptionState& exception_state) { 216 ExceptionState& exception_state) {
207 if (!target || !target->GetSecurityContext()) 217 if (!target || !target->GetSecurityContext())
208 return false; 218 return false;
209 return CanAccessFrame(accessing_window, 219 return CanAccessWindow(accessing_window, target->DomWindow(),
210 target->GetSecurityContext()->GetSecurityOrigin(), 220 exception_state);
211 target->DomWindow(), exception_state);
212 } 221 }
213 222
214 bool BindingSecurity::ShouldAllowAccessToFrame( 223 bool BindingSecurity::ShouldAllowAccessToFrame(
215 const LocalDOMWindow* accessing_window, 224 const LocalDOMWindow* accessing_window,
216 const Frame* target, 225 const Frame* target,
217 ErrorReportOption reporting_option) { 226 ErrorReportOption reporting_option) {
218 if (!target || !target->GetSecurityContext()) 227 if (!target || !target->GetSecurityContext())
219 return false; 228 return false;
220 return CanAccessFrame(accessing_window, 229 return CanAccessWindow(accessing_window, target->DomWindow(),
221 target->GetSecurityContext()->GetSecurityOrigin(), 230 reporting_option);
222 target->DomWindow(), reporting_option);
223 }
224
225 bool BindingSecurity::ShouldAllowAccessToDetachedWindow(
226 const LocalDOMWindow* accessing_window,
227 const DOMWindow* target,
228 ExceptionState& exception_state) {
229 CHECK(target && !target->GetFrame())
230 << "This version of shouldAllowAccessToFrame() must be used only for "
231 << "detached windows.";
232 if (!target->IsLocalDOMWindow())
233 return false;
234 Document* document = ToLocalDOMWindow(target)->document();
235 if (!document)
236 return false;
237 return CanAccessFrame(accessing_window, document->GetSecurityOrigin(), target,
238 exception_state);
239 } 231 }
240 232
241 bool BindingSecurity::ShouldAllowNamedAccessTo( 233 bool BindingSecurity::ShouldAllowNamedAccessTo(
242 const DOMWindow* accessing_window, 234 const DOMWindow* accessing_window,
243 const DOMWindow* target_window) { 235 const DOMWindow* target_window) {
244 const Frame* accessing_frame = accessing_window->GetFrame(); 236 const Frame* accessing_frame = accessing_window->GetFrame();
245 DCHECK(accessing_frame); 237 DCHECK(accessing_frame);
246 DCHECK(accessing_frame->GetSecurityContext()); 238 DCHECK(accessing_frame->GetSecurityContext());
247 const SecurityOrigin* accessing_origin = 239 const SecurityOrigin* accessing_origin =
248 accessing_frame->GetSecurityContext()->GetSecurityOrigin(); 240 accessing_frame->GetSecurityContext()->GetSecurityOrigin();
(...skipping 24 matching lines...) Expand all
273 // https://html.spec.whatwg.org/multipage/browsers.html#security-location, 265 // https://html.spec.whatwg.org/multipage/browsers.html#security-location,
274 // cross-origin script access to a few properties of Location is allowed. 266 // cross-origin script access to a few properties of Location is allowed.
275 // Location already implements the necessary security checks. 267 // Location already implements the necessary security checks.
276 if (type->Equals(&V8Location::wrapperTypeInfo)) 268 if (type->Equals(&V8Location::wrapperTypeInfo))
277 return true; 269 return true;
278 270
279 v8::Isolate* isolate = creation_context->GetIsolate(); 271 v8::Isolate* isolate = creation_context->GetIsolate();
280 LocalFrame* frame = ToLocalFrameIfNotDetached(creation_context); 272 LocalFrame* frame = ToLocalFrameIfNotDetached(creation_context);
281 ExceptionState exception_state(isolate, ExceptionState::kConstructionContext, 273 ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
282 type->interface_name); 274 type->interface_name);
275 // TODO(dcheng): Why doesn't this code just use DOMWindows throughout? Can't
276 // we just always use ToLocalDOMWindow(creation_context)?
283 if (!frame) { 277 if (!frame) {
284 // Sandbox detached frames - they can't create cross origin objects. 278 // Sandbox detached frames - they can't create cross origin objects.
285 LocalDOMWindow* calling_window = CurrentDOMWindow(isolate); 279 LocalDOMWindow* calling_window = CurrentDOMWindow(isolate);
286 LocalDOMWindow* target_window = ToLocalDOMWindow(creation_context); 280 LocalDOMWindow* target_window = ToLocalDOMWindow(creation_context);
287 281
288 return ShouldAllowAccessToDetachedWindow(calling_window, target_window, 282 // TODO(https://crbug.com/723057): This is tricky: this intentionally uses
289 exception_state); 283 // the internal CanAccessWindow() helper rather than ShouldAllowAccessTo().
284 // ShouldAllowAccessTo() unconditionally denies access if the DOMWindow is
285 // not attached to a Frame, but this code is intended for handling the
286 // detached DOMWindow case.
287 return CanAccessWindow(calling_window, target_window, exception_state);
290 } 288 }
291 const DOMWrapperWorld& current_world = 289 const DOMWrapperWorld& current_world =
292 DOMWrapperWorld::World(isolate->GetCurrentContext()); 290 DOMWrapperWorld::World(isolate->GetCurrentContext());
293 CHECK_EQ(current_world.GetWorldId(), 291 CHECK_EQ(current_world.GetWorldId(),
294 DOMWrapperWorld::World(creation_context).GetWorldId()); 292 DOMWrapperWorld::World(creation_context).GetWorldId());
295 293
296 return !current_world.IsMainWorld() || 294 return !current_world.IsMainWorld() ||
297 ShouldAllowAccessToFrame(CurrentDOMWindow(isolate), frame, 295 ShouldAllowAccessToFrame(CurrentDOMWindow(isolate), frame,
298 exception_state); 296 exception_state);
299 } 297 }
(...skipping 19 matching lines...) Expand all
319 } 317 }
320 318
321 void BindingSecurity::InitWrapperCreationSecurityCheck() { 319 void BindingSecurity::InitWrapperCreationSecurityCheck() {
322 WrapperCreationSecurityCheck::SetSecurityCheckFunction( 320 WrapperCreationSecurityCheck::SetSecurityCheckFunction(
323 &ShouldAllowAccessToCreationContext); 321 &ShouldAllowAccessToCreationContext);
324 WrapperCreationSecurityCheck::SetRethrowExceptionFunction( 322 WrapperCreationSecurityCheck::SetRethrowExceptionFunction(
325 &RethrowCrossContextException); 323 &RethrowCrossContextException);
326 } 324 }
327 325
328 void BindingSecurity::FailedAccessCheckFor(v8::Isolate* isolate, 326 void BindingSecurity::FailedAccessCheckFor(v8::Isolate* isolate,
329 const Frame* target) { 327 const WrapperTypeInfo* type,
330 // TODO(dcheng): See if this null check can be removed or hoisted to a 328 v8::Local<v8::Object> holder) {
331 // different location. 329 DOMWindow* target = FindWindow(isolate, type, holder);
332 if (!target) 330 // Failing to find a target means something is wrong. Failing to throw an
331 // exception could be a security issue, so just crash.
332 CHECK(target);
333
334 // TODO(https://crbug.com/723057): This is intended to match the legacy
335 // behavior of when access checks revolved around Frame pointers rather than
336 // DOMWindow pointers. This prevents web-visible behavior changes, since the
337 // previous implementation had to follow the back pointer to the Frame, and
338 // would have to early return when it was null.
339 if (!target->GetFrame())
333 return; 340 return;
334 341
335 DOMWindow* target_window = target->DomWindow();
336
337 // TODO(dcheng): Add ContextType, interface name, and property name as 342 // TODO(dcheng): Add ContextType, interface name, and property name as
338 // arguments, so the generated exception can be more descriptive. 343 // arguments, so the generated exception can be more descriptive.
339 ExceptionState exception_state(isolate, ExceptionState::kUnknownContext, 344 ExceptionState exception_state(isolate, ExceptionState::kUnknownContext,
340 nullptr, nullptr); 345 nullptr, nullptr);
341 exception_state.ThrowSecurityError( 346 exception_state.ThrowSecurityError(
342 target_window->SanitizedCrossDomainAccessErrorMessage( 347 target->SanitizedCrossDomainAccessErrorMessage(CurrentDOMWindow(isolate)),
343 CurrentDOMWindow(isolate)), 348 target->CrossDomainAccessErrorMessage(CurrentDOMWindow(isolate)));
344 target_window->CrossDomainAccessErrorMessage(CurrentDOMWindow(isolate)));
345 } 349 }
346 350
347 } // namespace blink 351 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698