js_lis 3.0.1 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +16 -0
- package/VirtualKeyboard.js +40 -27
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -21,6 +21,14 @@ It is intended for high-risk input scenarios where users want an additional defe
|
|
|
21
21
|
- It is also intended to reduce risk from interception workflows commonly demonstrated with network attack tools such as **bettercap** (for example, injected key-capture scripts in compromised environments).
|
|
22
22
|
- No client-side tool can guarantee complete protection. Security still depends on HTTPS, trusted networks, endpoint hygiene, and browser integrity.
|
|
23
23
|
|
|
24
|
+
### Key Security Features
|
|
25
|
+
|
|
26
|
+
- **AES-GCM Memory Encryption**: All keystrokes are buffered and encrypted in-memory using the Web Crypto API (AES-256-GCM). The actual DOM elements never store the plaintext values.
|
|
27
|
+
- **Shadow DOM Isolation**: The keyboard UI is rendered inside a `closed` Shadow DOM to prevent malicious external CSS/JS from sniffing or tampering with the keys.
|
|
28
|
+
- **Anti-Clipboard Sniffing**: Blocks unauthorized `copy`, `cut`, and `execCommand("copy"/"cut")` actions to prevent clipboard hijacking.
|
|
29
|
+
- **IME Reconciliation**: Safely handles Input Method Editors (like Thai or Chinese typing) natively without exposing the secure buffer.
|
|
30
|
+
- **Dynamic Scrambling**: Provides an optional dynamic key scramble to thwart clickjacking or screen coordinate tracking.
|
|
31
|
+
|
|
24
32
|
### Installation
|
|
25
33
|
|
|
26
34
|
```bash
|
|
@@ -91,6 +99,14 @@ window.addEventListener("DOMContentLoaded", () => {
|
|
|
91
99
|
- รวมถึงสามารถช่วยลดความเสี่ยงจากรูปแบบการโจมตีที่มักสาธิตผ่านเครื่องมือเครือข่ายอย่าง **bettercap** (เช่น การแทรกสคริปต์เพื่อดักรับข้อมูลการป้อนในสภาพแวดล้อมที่ถูกบุกรุก)
|
|
92
100
|
- อย่างไรก็ตาม เครื่องมือฝั่งผู้ใช้เพียงอย่างเดียวไม่สามารถรับประกันการป้องกันได้สมบูรณ์ จำเป็นต้องใช้ร่วมกับ HTTPS, เครือข่ายที่เชื่อถือได้, ความปลอดภัยของเครื่องผู้ใช้ และความน่าเชื่อถือของเบราว์เซอร์
|
|
93
101
|
|
|
102
|
+
### ฟีเจอร์ด้านความปลอดภัยที่สำคัญ
|
|
103
|
+
|
|
104
|
+
- **การเข้ารหัสหน่วยความจำด้วย AES-GCM**: ทุกการกดแป้นพิมพ์จะถูกบัฟเฟอร์และเข้ารหัสในหน่วยความจำชั่วคราวด้วย Web Crypto API (AES-256-GCM) ข้อความต้นฉบับจะไม่ถูกเก็บเป็น plaintext ใน DOM elements
|
|
105
|
+
- **การแยกส่วนด้วย Shadow DOM**: หน้าคีย์บอร์ดจะถูกเรนเดอร์อยู่ภายใน `closed` Shadow DOM เพื่อป้องกันไม่ให้ CSS/JS จากภายนอกแอบอ่านหรือแก้ไขปุ่มคีย์บอร์ด
|
|
106
|
+
- **ป้องกันการแอบอ่านคลิปบอร์ด (Anti-Clipboard Sniffing)**: บล็อกการดักจับคำสั่ง `copy`, `cut`, และ `execCommand` ที่ไม่ได้รับอนุญาตเพื่อป้องกันการขโมยข้อมูลจากคลิปบอร์ด
|
|
107
|
+
- **รองรับ IME อย่างปลอดภัย**: จัดการกับการพิมพ์ผ่าน Input Method Editors (เช่น ภาษาไทย) ได้อย่างถูกต้องโดยที่ยังคงแยกบักเฟอร์ข้อมูลอย่างปลอดภัย
|
|
108
|
+
- **การสลับตำแหน่งปุ่ม (Dynamic Scrambling)**: มีฟังก์ชันสลับตำแหน่งปุ่มแบบสุ่มเพื่อป้องกันการติดตามพิกัดการคลิกบนหน้าจอหรือ clickjacking
|
|
109
|
+
|
|
94
110
|
### การติดตั้ง
|
|
95
111
|
|
|
96
112
|
```bash
|
package/VirtualKeyboard.js
CHANGED
|
@@ -1,27 +1,32 @@
|
|
|
1
1
|
import { layouts } from "./layouts.js";
|
|
2
2
|
import { getCSS, injectCSS } from "./style/kbstyle.js";
|
|
3
3
|
|
|
4
|
-
//AES-GCM ผ่าน WeakMap
|
|
4
|
+
//AES-GCM ผ่าน WeakMap พร้อม Fallback สำหรับ HTTP
|
|
5
5
|
class SecureBufferStore {
|
|
6
6
|
constructor() {
|
|
7
7
|
this._store = new WeakMap();
|
|
8
|
+
this.hasCrypto = typeof crypto !== 'undefined' && !!crypto.subtle;
|
|
8
9
|
}
|
|
9
10
|
|
|
10
11
|
//สร้างกุญแจเข้ารหัส
|
|
11
12
|
async _getEntry(inputEl) {
|
|
12
13
|
if (!this._store.has(inputEl)) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
if (this.hasCrypto) {
|
|
15
|
+
const key = await crypto.subtle.generateKey(
|
|
16
|
+
{ name: "AES-GCM", length: 256 },
|
|
17
|
+
false, // ไม่ export ออก
|
|
18
|
+
["encrypt", "decrypt"]
|
|
19
|
+
);
|
|
20
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
21
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
22
|
+
{ name: "AES-GCM", iv },
|
|
23
|
+
key,
|
|
24
|
+
new TextEncoder().encode("")
|
|
25
|
+
);
|
|
26
|
+
this._store.set(inputEl, { type: 'crypto', key, iv, ciphertext });
|
|
27
|
+
} else {
|
|
28
|
+
this._store.set(inputEl, { type: 'fallback', text: '' });
|
|
29
|
+
}
|
|
25
30
|
}
|
|
26
31
|
return this._store.get(inputEl);
|
|
27
32
|
}
|
|
@@ -29,26 +34,34 @@ class SecureBufferStore {
|
|
|
29
34
|
//ถอดรหัสชั่วคราว
|
|
30
35
|
async read(inputEl) {
|
|
31
36
|
if (!this._store.has(inputEl)) return "";
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
const entry = this._store.get(inputEl);
|
|
38
|
+
if (entry.type === 'crypto') {
|
|
39
|
+
try {
|
|
40
|
+
const plain = await crypto.subtle.decrypt({ name: "AES-GCM", iv: entry.iv }, entry.key, entry.ciphertext);
|
|
41
|
+
return new TextDecoder().decode(plain);
|
|
42
|
+
} catch {
|
|
43
|
+
return "";
|
|
44
|
+
}
|
|
45
|
+
} else {
|
|
46
|
+
return entry.text;
|
|
38
47
|
}
|
|
39
48
|
}
|
|
40
49
|
|
|
41
50
|
//เข้ารหัสใหม่
|
|
42
51
|
async write(inputEl, plaintext) {
|
|
43
52
|
const entry = await this._getEntry(inputEl);
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
53
|
+
if (entry.type === 'crypto') {
|
|
54
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
55
|
+
const ciphertext = await crypto.subtle.encrypt(
|
|
56
|
+
{ name: "AES-GCM", iv },
|
|
57
|
+
entry.key,
|
|
58
|
+
new TextEncoder().encode(plaintext)
|
|
59
|
+
);
|
|
60
|
+
entry.iv = iv;
|
|
61
|
+
entry.ciphertext = ciphertext;
|
|
62
|
+
} else {
|
|
63
|
+
entry.text = plaintext;
|
|
64
|
+
}
|
|
52
65
|
}
|
|
53
66
|
|
|
54
67
|
//ค่าเริ่มต้นให้กับ Input
|
package/package.json
CHANGED