HiI'm planning to implement a mechanism to enable developers to write Blink features in JavaScript (a.k.a. Blink-in-JavaScript).- A design document: https://docs.google.com/a/google.com/document/d/13cT9Klgvt_ciAR3ONGvzKvw6fz9-f6E0FrqYFqfoc8Y/edit#- An example CL to move C++ implementation of Window.atob()/btoa() to JavaScript: https://codereview.chromium.org/138223002/The basic idea of Blink-in-JavaScript is that we want to implement only the core part of Blink in C++ and implement other parts in JavaScript on top of existing, web-exposed JavaScript APIs. By implementing more things in JavaScript, we can improve security, maintainability and programmability of Blink. Blink-in-JavaScript is executed in the same security model as content scripts of Chrome extensions. For more details, look at the design document.
At the moment, I'm thinking about Document.execCommand() as a first target of Blink-in-JavaScript.
Currently Document.execCommand() complicates the architecture of editing/, so we want to factor out the complexity from C++. Most editing commands of Document.execCommand() can be implemented on top of web-exposed JavaScript APIs. The editing team already started rewriting Document.execCommand() in JavaScript.I'd be happy if you could tell me more use cases and improve the design. Comments are appreciated!--
Kentaro Hara, Tokyo, Japan
To unsubscribe from this group and stop receiving emails from it, send an email to blink-dev+...@chromium.org.
If you want a challenge problem, you might try to implement XSLTProcessor using Blink-in-JavaScript. You'll probably need to compile libxslt and libxml using Emscripten and load that into the isolated world. Bonus points if you implement the XSLT processing directive using Blink-in-JavaScript. :)
On Thu, Jan 16, 2014 at 7:42 PM, Kentaro Hara <har...@chromium.org> wrote:
HiI'm planning to implement a mechanism to enable developers to write Blink features in JavaScript (a.k.a. Blink-in-JavaScript).- A design document: https://docs.google.com/a/google.com/document/d/13cT9Klgvt_ciAR3ONGvzKvw6fz9-f6E0FrqYFqfoc8Y/edit#- An example CL to move C++ implementation of Window.atob()/btoa() to JavaScript: https://codereview.chromium.org/138223002/The basic idea of Blink-in-JavaScript is that we want to implement only the core part of Blink in C++ and implement other parts in JavaScript on top of existing, web-exposed JavaScript APIs. By implementing more things in JavaScript, we can improve security, maintainability and programmability of Blink. Blink-in-JavaScript is executed in the same security model as content scripts of Chrome extensions. For more details, look at the design document.
At the moment, I'm thinking about Document.execCommand() as a first target of Blink-in-JavaScript. Currently Document.execCommand() complicates the architecture of editing/, so we want to factor out the complexity from C++. Most editing commands of Document.execCommand() can be implemented on top of web-exposed JavaScript APIs. The editing team already started rewriting Document.execCommand() in JavaScript.I'd be happy if you could tell me more use cases and improve the design. Comments are appreciated!--
Kentaro Hara, Tokyo, Japan
Are there plans to build in some sort of deferred initialization so the browser doesn't have to parse all that code for every document? (From the design document, it seems like that could already be the case.)
Are there plans to build in some sort of deferred initialization so the browser doesn't have to parse all that code for every document? (From the design document, it seems like that could already be the case.)Blink-in-JavaScript is compiled lazily on a class-by-class basis (A class is a unit installed by an installClass() method. See an example in the document). At the first time user's JavaScript accesses window.atob(), WindowBase64.js is compiled and the compiled code is cached. At the second time user's JavaScript accesses window.atob(), the compiled code is used.
I'd argue that you'd be introducing potential security problems.It's not true that we do not leak objects between worlds. We don't do it intentionally, but bugs are bound to creep in, as they have in the past. Currently, though, those bugs are exposed only to user extensions, whereas what you propose could be executed by any js on the web, making the bugs significantly more serious/exploitable.I think you should consider the following counterproposal to mitigate the security risk:
- install a native callback for the function, as we do now, but make that function be a generic one (called lazyInstallProperty or something) with a data argument that is a the name of a script to execute
- on first execution of that function, do this
- compile the js needed for property
- overwrite the old property with the new property and run the function again
This has a number of advantages over what you propose:
- no wrapping required
- no security risks above what any user can already do
- slightly faster than what you propose, although that's kind of irrelevant
- no need to reason about what is happening between isolated worlds
- unused functions would be garbage collected instead of kept alive forever in the blinkjs cache
v8's internal compile cache should take of not duplicating the code everywhere, so that's not an issue
The only disadvantage I can think of is that the people writing the js have to be extremely careful not to use global variables on the page, which is very hard to do in js. We'd probably need to have support from v8 to ensure that. Emscripten'd code could be trivially made safe.
I think you should consider the following counterproposal to mitigate the security risk:
- install a native callback for the function, as we do now, but make that function be a generic one (called lazyInstallProperty or something) with a data argument that is a the name of a script to execute
- on first execution of that function, do this
- compile the js needed for property
- overwrite the old property with the new property and run the function again
I'd argue that you'd be introducing potential security problems.
It's not true that we do not leak objects between worlds. We don't do it intentionally, but bugs are bound to creep in, as they have in the past. Currently, though, those bugs are exposed only to user extensions, whereas what you propose could be executed by any js on the web, making the bugs significantly more serious/exploitable.
whereas what you propose could be executed by any js on the web, making the bugs significantly more serious/exploitable.
I think you should consider the following counterproposal to mitigate the security risk:
1. install a native callback for the function, as we do now, but make that function be a generic one (called lazyInstallProperty or something) with a data argument that is a the name of a script to execute
2. on first execution of that function, do this
- compile the js needed for property
- overwrite the old property with the new property and run the function again
This has a number of advantages over what you propose:
1. no wrapping required
2. no security risks above what any user can already do
3. slightly faster than what you propose, although that's kind of irrelevant
4. no need to reason about what is happening between isolated worlds
5. unused functions would be garbage collected instead of kept alive forever in the blinkjs cache
This is not quite true. Blink-in-JavaScript executes only JavaScripts that are developed and reviewed by Blink developers. Blink-in-JavaScript is not a thing that executes any untrusted JavaScripts on the web.
If I'm understanding your proposal correctly, in your proposal Blink-in-JavaScript and user's script will run in the same isolated world. Then, for example, what happens if user's script hijacks String.prototype.substr, and Blink-in-JavaScript calls String.prototype.substr with strings that should not be exposed to outside?
Stepping back, I guess I'm not sure what security risk you are trying to guard against with your proposal if you weren't expecting the BlinkInJS to have any increased privileges.
Calling into an isolated world is an edge case that receives very little testing and does not have sufficient guards against cross context leaks. BlinkInJs would allow someone to call code which, even if only executing dom functions on isolated worlds, could potentially leak a wrapper from some other world. If that world happened to be a main world that it not your own, it would be a security leak. I'm not saying it's likely to happen, just that it adds a lot more pressure on isolated worlds to be secure than they've had before.
Stepping back, I guess I'm not sure what security risk you are trying to guard against with your proposal if you weren't expecting the BlinkInJS to have any increased privileges.
Calling into an isolated world is an edge case that receives very little testing and does not have sufficient guards against cross context leaks. BlinkInJs would allow someone to call code which, even if only executing dom functions on isolated worlds, could potentially leak a wrapper from some other world. If that world happened to be a main world that it not your own, it would be a security leak. I'm not saying it's likely to happen, just that it adds a lot more pressure on isolated worlds to be secure than they've had before. If we had a way to guarantee their security, then they would certainly be better than what I proposed.
I don't have enough experience with the content script security model to chime in on whether that is secure enough as an alternative or not.
1) Back when we introduced content scripts, we had the idea that there could be low-level CHECK's that code from one isolated world could never touch objects from another world. I'm not sure if it was ever implemented though. If that existed, then I think it would increase confidence in relying on isolated worlds for security.
2) There is a generalization of the your idea that would both increase its flexibility and perhaps allay security concerns, particularly if the CHECKs in (1) are not possible for some reason:
The only reason you need isolated worlds at all is because you want to be able to pass DOM wrappers between user JavaScript and BlinkInJavaScript. If it weren't for this fact, then you wouldn't need isolated worlds because the only other types you propose that the C++ bindings layer would allow through are primitives. Your bindings layer is providing some isolation, but it only works for primitives.
Instead of relying on isolated worlds for the complex objects case, you could teach the C++ bindings layer to do isolation for them too. Basically for every object and function implemented in BlinkInJS exposed to user JS, there would be a C++ proxy representing the target object. When user JS tried to access a property of the target, or call a function, the C++ proxy would actually receive the access, delegate to the target object, and marshall values back and forth. The proxies would be recursive, so this would work for arbitrary data structures. As before, you could just allow primitive JS values through, so there would not be serialization overhead.
Thanks aaron!1) Back when we introduced content scripts, we had the idea that there could be low-level CHECK's that code from one isolated world could never touch objects from another world. I'm not sure if it was ever implemented though. If that existed, then I think it would increase confidence in relying on isolated worlds for security.Yes, this would be the best. It's possible to insert the check, but it's never trivial to insert the check without adding any performance overhead. We have to insert the check to all points where we return DOM wrappers to JavaScript. But it's worth considering.
Haraken,How is this related to V8 extensions?
Thanks aaron!
1) Back when we introduced content scripts, we had the idea that there could be low-level CHECK's that code from one isolated world could never touch objects from another world. I'm not sure if it was ever implemented though. If that existed, then I think it would increase confidence in relying on isolated worlds for security.Yes, this would be the best. It's possible to insert the check, but it's never trivial to insert the check without adding any performance overhead. We have to insert the check to all points where we return DOM wrappers to JavaScript. But it's worth considering.2) There is a generalization of the your idea that would both increase its flexibility and perhaps allay security concerns, particularly if the CHECKs in (1) are not possible for some reason:The only reason you need isolated worlds at all is because you want to be able to pass DOM wrappers between user JavaScript and BlinkInJavaScript. If it weren't for this fact, then you wouldn't need isolated worlds because the only other types you propose that the C++ bindings layer would allow through are primitives. Your bindings layer is providing some isolation, but it only works for primitives.Right. To support primitive values, we just need to isolate contexts. To support DOM wrappers, we need to introduce isolated worlds in addition.Instead of relying on isolated worlds for the complex objects case, you could teach the C++ bindings layer to do isolation for them too. Basically for every object and function implemented in BlinkInJS exposed to user JS, there would be a C++ proxy representing the target object. When user JS tried to access a property of the target, or call a function, the C++ proxy would actually receive the access, delegate to the target object, and marshall values back and forth. The proxies would be recursive, so this would work for arbitrary data structures. As before, you could just allow primitive JS values through, so there would not be serialization overhead.This sounds interesting, but it seems to me that the C++ proxy objects are doing almost the same thing as isolated worlds. I agree that the C++ proxy objects will allow us to overcome the security concerns of Blink-in-JavaScript even if we have bugs in the current implementation of isolated worlds, but it seems that it is saying "Because the current isolated worlds might have bugs, let's implement another kind of isolated world (i.e., C++ proxy objects) for Blink-in-JavaScript". Then, I still think that we should try our best to harden security of the current isolated worlds instead of introducing another kind of isolated world. (Sorry if I'm missing your point.)
JavaScript proxy object as massively more difficult to implement correctly than isolated worlds. If you don't believe our isolated worlds implementation is correct, then you shouldn't believe we could implement JS proxy objects correctly. :)
JavaScript proxy object as massively more difficult to implement correctly than isolated worlds. If you don't believe our isolated worlds implementation is correct, then you shouldn't believe we could implement JS proxy objects correctly. :)I think that the proxy objects in Mozilla are more complicated because of the fact that their goal -- at least the ones I have seen -- is not to do full isolation, but to allow a kind of one-way membrane. I don't see why it is so hard to do full isolation with proxies. Can you provide a concrete example?
IMHO, we should just fix whatever bugs remain in the isolated worlds mechanism and add some more RELEASE_ASSERTs. That's beneficial both for Chrome extensions and for this mechanism. Perhaps we could start by running the RELEASE_ASSERT for the Blink-in-JavaScript isolated world. That way we'd be assured there aren't any leaks for those scripts and we'll have a useful bisect data if/when we trigger the assert in the future.
Or, another example (sorry if my WebIDL is rusty):
[BlinkInJavaScript]
interface Foo {
DOMString getBar();
void setBar(DOMString s);
};
[supplemental]
interface FooWindow {
Foo getFoo();
};
Can you do this in your design? It's not clear how it would be accomplished from the doc.