RustでBaiduWenkuの「Plusバージョン」を実装するために使用したものについて

チャンス

最近、クラスメートが書いた百度文庫のテキストコピーのオイルモンキースクリプトが再び失敗しました。この機会に、百度文庫の反テキストコピー戦略を再分析して、百度文庫がこの需要を低コストでどのように達成できるかを確認しました。

BaiduWenkuのアンチテキストコピー戦略をご覧ください

1.テキストがどのようにレンダリングされるかを確認します

image-20220323141046219.png

Baidu Libraryを開き、ランダムにドキュメントを選択すると、各ドキュメントがキャンバスにレンダリングされている要素を調べることで確認できます。キャンバスは、テキストを直接描画するためのインターフェイスfillText 1を開くため、テキスト情報をキャンバスに直接描画するのは非常に簡単です。キャンバスに描かれているテキストは選択できない画像であるため、テキストコピーの問題は根本的に解決されています。

逸脱:以前のバージョンのBaidu Wenkuは、ダブルキャンバスを使用して、キャンバスにレンダリングされたテキスト情報を元の情報にプッシュバックし、上部のキャンバスでマウスイベントを監視し、テキストの選択とコピーの効果をシミュレートしましたが、このバージョンはオイルモンキースクリプトがキャンセルされるのを防ぎます(推測)。

2.データソースを確認します

したがって、キャンバスの描画に使用されるデータソースはどこからのものであり、サーバーから直接取得することはできません。サーバーから直接取得する場合は、画像またはsvgである可能性が高く、サーバーのコストが増加し、削減されます。ユーザーエクスペリエンス(明らかに程度と量)。さらに、コンソールネットワークパネルを介していくつかの疑わしいパケットを見つけることができます。image-20220323144506851.png

これらの長いJsonドキュメントは、canvasに必要な描画情報であり、キーフィールドのみを分析します。その中で、フィールドcunicodeエンコードされたテキストであり、フィールドpはテキストの幅、高さ、および位置情報であり、残りのフィールドはテキストスタイルとドキュメント形式に関連している可能性があります。したがって、サーバーから要求されたJsonデータを解析することで、完全なキャンバス描画情報を取得できます。

3.プロセス全体の大まかな概要を把握します

最初の2つのステップの分析に基づいて、アンチテキストコピーを実現するためのBaidu Wenkuの全体的なフロントエンドプロセスを大まかに推測できます。これらのプロセスは、フロントエンドjsファイルに1つずつあります。具体的な実装の詳細ここでは説明しません。1.png

4.問題

由于 Json 数据是完全明文的,所以恶意脚本或插件完全可以自己通过解析 Json 数据来得到完整的文档数据,这个成本是比较低的。另一方面,如果想要对 Json 数据加密后再传输,那么若解密函数使用 js 编写,即使对 js 脚本做混淆加密,破解其解密函数也是一件成本很低的工作。

实现一个百度文库 "Plus 版"

基于上述分析可以总结我们的需求为,想要在实现防文本复制的同时,对原始数据进行加密,并且提高关键解密函数的破解成本

1. 技术选型

使用 js 编写的脚本文件,无论通过何等混淆加密最后都将是一个可下断调试的脚本文件,因此想要定位到关键函数是比较轻松的一件事情,并且通过动态调试也能很快的跟出解密流程,所以我们不能把解密函数和解密后的明文数据存放在 js 文件和堆栈中。

为此,我们选择了以 WebAssembly 技术2 (以下简称 wasm)为主体的解决方案,由于 wasm 是一种二进制的格式,可读性差,且无法通过下断的方式进行调试,因此想要通过静态分析还原出关键函数是一件成本较高的事情。 编写 wasm 的方式有很多种,受文章《RUST 是 JavaScript 基建的未来》3的鼓舞,我选择了使用 Rust 作为 wasm 的开发语言,且 Rust 已经有了针对 wasm 的较为完整的工具链4

2. 整体流程

下面,我们给出了基于 wasm 改造后的整体流程,将关键步骤下放到 wasm 层中进行,包括 dom 的相关操作。特别地,我们考虑对用户权限进行划分,VIP 用户可以享受直接复制文本的功能。

2.png

3. 关键技术点分析

首先,我们参照百度文库定义一下原始信息,其中 cipher 为密文,position 为定位信息,font_style 为文本样式。

const info = [
  {
    cipher: "\u{1d}\u{14}\u{14}",
    position: { x: 30, y: 30 },
    font_style: { size: 16 },
  },
  {
    cipher: "\u{19}\u{1a}\t",
    position: { x: 50, y: 50 },
    font_style: { size: 30 },
  },
];
复制代码

定义 js 层与 wasm 层的接口为 encrypt_canvas,传入渲染所需加密信息和用户身份令牌。

encrypt_canvas({ render_info: info, user_token: "1234567" });
复制代码

在 wasm 层,我们通过调用解密函数来得到明文信息,并通过 web-sys 来操作 dom。最终我们实现了三类视图,它们分别基于标签 canvasimagediv

其中,canvas 调用了绘图指令,image调用了canavas.to_data_url()方法5div则是使用绝对定位来完成布局。具体实现的源码参见附录,此处不再讨论具体实现细节。

4. 效果展示

画像-20220323162854815.png

浅谈我理解的前端安全

在前端想要实现绝对的安全是不可能的,因为所有运行所需的数据和信息都已经下载到前端了,想要逆向它只是时间问题。但是,我们可以通过增加较小的反逆向成本来大幅提高逆向的成本,从而打破它们之间的平衡。

这让我想起了,在逆向一个 Android 应用时,由 Java 虚拟机生成的字节码可以通过工具轻松逆向为 Smali 这种可读性较强的语言,并且通过插桩等形式动态地调试代码。为了提高应用的安全性,开发者将关键函数都封装在 So 库中,此时 hacker 可以通过附加等形式动态调试由 ARM 指令集组成的汇编代码,此时代码的可读性已经非常差了,逆向成本大大增加。如果再将通过一些混淆加壳等形式对 So 库进行加密保护,那么将进一步提高逆向门槛。

私の経験からすると、セキュリティの観点からのみ、Javaとsoライブラリの関係はjsとwasmの関係に似ていると思います(ただし、wasmは誕生当初はフロントエンドセキュリティ用に準備されていない可能性があります)。動的デバッグはまだ見つかりません。wasm方式の場合、wasm形式で難読化やパッキングなどの操作ができれば、セキュリティはさらに保証されます。

付録

  1. 特定の実装ソースコードgithub.com/ascodelife/…

参考記事

  1. キャンバスfillTextインターフェース-developer.mozilla.org/en-US/docs/…
  2. WebAssemblyの概要-developer.mozilla.org/en-US/docs/…
  3. RUSTはJavaScriptインフラストラクチャの未来です-juejin.cn/post/703099…
  4. wasm - bindgenドキュメント-rustwasm.github.io/wasm-bindge…
  5. キャンバスtoDataURL関数-developer.mozilla.org/en-US/docs/…

おすすめ

転載: juejin.im/post/7078299612972318727