Создаем расширение для защиты от фишинга

D2

Администратор
Регистрация
19 Фев 2025
Сообщения
4,380
Реакции
0
Приветствую, форумчане! Сегодня напишу инструкцию по созданию и запуску расширения для браузера Chrome, которое помогает защитить пользователей от одной из самых распространенных угроз в интернете - фишинга. Проект (решил дать собирательное название AntiPhish Guard) будет использовать комбинацию различных техник для выявления потенциально опасных сайтов.

Для чего же нам создавать такое расширение? Фишинг до сих пор является основной причиной утечек данных. Почти всегда технически не подкованные пользователи сами отдают все доступы и информацию, потому что банально не могут правильно и вовремя определить фишинговое письмо. Если мы говорим про фишинговые сайты, то год за годом они становятся все более похожими на оригинальные, что крайне затрудняет их автоматическое обнаружение. В среднем ежегодно регистрируется около двух миллионов таких сайтов (не удивительно, когда можно скопировать сайт буквально за несколько минут с помощью необходимых инструментов). Именно поэтому создание эффективного инструмента для защиты от фишинга является актуальной и сложной задачей. Конечно же есть куча уже готовых решений, но знать БАЗУ необходимо, а уметь самому собрать еще и полезно!

Перед началом давайте рассмотрим основные функции расширения, которые у меня получилось реализовать:
  1. Проверка URL через API Google Safe Browsing
  2. Анализ содержимого страницы на наличие подозрительных элементов
  3. Проверка SSL-сертификатов
  4. Интеграция с VirusTotal для дополнительной проверки
  5. Ведение пользовательских черных и белых списков
  6. Автоматическое обновление базы данных известных фишинговых сайтов
  7. Настройки пользователя для тонкой настройки уровня защиты
  8. Статистика заблокированных угроз
  9. Возможность экспорта и импорта пользовательских списков
Ниже постарался максимально подробно расписать инструкцию по написанию и внедрению.


Первым делом необходимо будет создать структуру проекта. Для этого откройте терминал и выполните следующие команды: создайте директорию командой mkdir antiphish-guard, далее провалитесь в директорию cd antiphish-guard, создайте папку mkdir images внутри директории, и создайте все необходимые файлы touch manifest.json background.js content.js popup.html popup.js options.html options.js warning.html warning.js blocked.html safeBrowsing.js test_phishing.html contentAnalyzer.js. После создания всех необходимых элементов, начнем каждый наполнять кодом с необходимым функционалом.

Выглядеть должно таким образом:

Рисунок1.png



Вторым шагом будет создание первого файла и сердца любого расширения – manifest.json, он определяет основные свойства расширения, запрашиваемые разрешения и структуру проекта:

JavaScript: Скопировать в буфер обмена
Код:
{
  "manifest_version": 2,
  "name": "AntiPhish Guard",
  "version": "1.0",
  "description": "Защита от фишинговых атак",
  "permissions": [
    "activeTab",
    "storage",
    "webNavigation",
    "tabs",
    "alarms",
    "notifications",
    "downloads"
  ],
  "background": {
    "scripts": ["background.js"],
    "persistent": false
  },
  "browser_action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "images/icon16.png",
      "48": "images/icon48.png",
      "128": "images/icon128.png"
    }
  },
  "icons": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ],
  "options_page": "options.html",
  "incognito": "split"
}

После каждого кода буду разбирать ключевые функции (где-то коротко, где-то более подробно). Функция manifest_version указывает версию формата манифеста, я использовал вторую. Список разрешений permissions - требуется для работы расширения. Запрашивает доступ к активной вкладке, хранилищу данных, навигации, управлению вкладками, уведомлениям и возможности скачивания файлов. Скрипт background необходим для работы в фоновом режиме (я установил persistent: false, чтобы использовать событийно-ориентированную модель вместо постоянно работающего фонового процесса). browser_action определяет действие, которое будет выполняться при нажатии на иконку расширения, включая открытие popup.html. Скрипт content_scripts будет внедряться на все посещаемые страницы. options_page определяет страницу настроек расширения. incognito и так всем понятно, а значение split позволяет расширению работать в режиме инкогнито, но с отдельным хранилищем данных.


Далее переходим к реализации следующего файла - фонового скрипта background.js. URL-анализатор - это наш передовой отряд в борьбе с фишингом. Его задача - молниеносно проверить адрес сайта, на который пытается зайти пользователь, и определить, не таится ли за ним угроза.

Я использовал асинхронные запросы к обоим API, чтобы не замедлять работу браузера. Вот как это работает:
  1. Пользователь переходит на новый URL.
  2. Расширение перехватывает этот переход и запускает процесс проверки.
  3. Отправляются параллельные запросы к Google Safe Browsing и VirusTotal.
  4. Пока идет проверка, страница начинает загружаться (без ухудшения пользовательского опыта).
  5. Если хотя бы один из сервисов сообщает об опасности, немедленно прерывает загрузку и показывает предупреждение.
Но что, если API недоступны или работают медленно? Здесь в игру вступает наша локальная база данных. Мы кэшируем результаты проверок и храним их локально в течение определенного времени (скажем, 24 часа). Это позволяет нам:
  • Ускорить проверку для часто посещаемых сайтов.
  • Обеспечить базовый уровень защиты даже при отсутствии интернет-соединения.
  • Снизить нагрузку на API и избежать ограничений на количество запросов.
Скрипт будет работать постоянно в фоновом режиме и отвечать за обработку событий навигации, проверку URL и координацию работы других компонентов расширения.

JavaScript: Скопировать в буфер обмена
Код:
let phishingDatabase = {};

chrome.runtime.onInstalled.addListener(() => {
    updatePhishingDatabase();
    chrome.alarms.create('updatePhishingDatabase', { periodInMinutes: 1440 });
});

chrome.alarms.onAlarm.addListener((alarm) => {
    if (alarm.name === 'updatePhishingDatabase') {
        updatePhishingDatabase();
    }
});

function updatePhishingDatabase() {
    fetch('https://api.example.com/phishing-databasehttps://safebrowsing.googleapis.com/v4/threatMatches:find')
        .then(response => response.json())
        .then(data => {
            phishingDatabase = data;
            chrome.storage.local.set({phishingDatabase: data});
        })
        .catch(error => console.error('Error updating phishing database:', error));
}

chrome.webNavigation.onBeforeNavigate.addListener((details) => {
    const url = new URL(details.url);
    chrome.storage.sync.get(['whitelist', 'blacklist'], (result) => {
        const whitelist = result.whitelist || [];
        const blacklist = result.blacklist || [];

        if (blacklist.includes(url.hostname)) {
            chrome.tabs.update(details.tabId, { url: "blocked.html" });
            showBlockNotification(url.href);
        } else if (!whitelist.includes(url.hostname)) {
            checkPhishing(url.href, details.tabId);
        }
    });
});

async function checkPhishing(url, tabId) {
    const cachedResult = await getCachedCheckResult(url);
    if (cachedResult !== null) {
        if (cachedResult === 'blocked') {
            chrome.tabs.update(tabId, { url: "blocked.html" });
            updateStatistics('blocked');
            showBlockNotification(url);
        } else if (cachedResult === 'warning') {
            showWarning(tabId, url);
            updateStatistics('warned');
        }
        return;
    }

    let result = 'safe';

    const googleSafeResult = await checkUrlWithGoogleSafeBrowsing(url);
    if (!googleSafeResult) {
        result = 'blocked';
    }


    if (phishingDatabase[url]) {
        result = 'blocked';
    }


    chrome.storage.sync.get(['enableVirusTotal'], async (data) => {
        if (data.enableVirusTotal !== false) {
            const virusTotalResult = await checkWithVirusTotal(url);
            if (virusTotalResult === 'suspicious') {
                result = 'warning';
            }
        }
    });


    chrome.storage.sync.get(['enableContentAnalysis'], (data) => {
        if (data.enableContentAnalysis !== false) {
            chrome.tabs.sendMessage(tabId, {action: "analyzeContent"}, (response) => {
                if (response && response.suspicious) {
                    result = 'warning';
                }
                finalizeCheck(result, url, tabId);
            });
        } else {
            finalizeCheck(result, url, tabId);
        }
    });
}

function finalizeCheck(result, url, tabId) {
    if (result === 'blocked') {
        chrome.tabs.update(tabId, { url: "blocked.html" });
        updateStatistics('blocked');
        showBlockNotification(url);
    } else if (result === 'warning') {
        showWarning(tabId, url);
        updateStatistics('warned');
    }
    setCachedCheckResult(url, result);
}

async function checkUrlWithGoogleSafeBrowsing(url) {
    const apiKey = 'GoogleApiKeys';
    const apiUrl = `https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${apiKey}`;
    
    const requestBody = {
        client: {
            clientId: "AntiPhish Guard",
            clientVersion: "1.0.0"
        },
        threatInfo: {
            threatTypes: ["MALWARE", "SOCIAL_ENGINEERING"],
            platformTypes: ["ANY_PLATFORM"],
            threatEntryTypes: ["URL"],
            threatEntries: [{ url: url }]
        }
    };

    try {
        const response = await fetch(apiUrl, {
            method: 'POST',
            body: JSON.stringify(requestBody)
        });
        const data = await response.json();
        return Object.keys(data).length === 0;
    } catch (error) {
        console.error('Error checking Google Safe Browsing API:', error);
        return true;
    }
}

async function checkWithVirusTotal(url) {
    const apiKey = 'VirusTotalAPIKeys';
    const apiUrl = `https://www.virustotal.com/vtapi/v2/url/report`;

    const params = new URLSearchParams({
        apikey: apiKey,
        resource: url
    });

    try {
        const response = await fetch(`${apiUrl}?${params}`);
        const data = await response.json();

        if (data.response_code === 1) {
            if (data.positives > 0) {
                return 'suspicious';
            }
        }
    } catch (error) {
        console.error('Error checking VirusTotal API:', error);
    }

    return 'safe';
}

function showWarning(tabId, url) {
    chrome.storage.local.set({lastBlockedUrl: url}, function() {
        chrome.tabs.update(tabId, {url: chrome.runtime.getURL("warning.html")});
    });
}

function updateStatistics(action) {
    chrome.storage.local.get(['statistics'], (result) => {
        let stats = result.statistics || {blocked: 0, warned: 0};
        stats[action]++;
        chrome.storage.local.set({statistics: stats});
    });
}

chrome.webNavigation.onCommitted.addListener(
    function(details) {
        if (details.frameId === 0) {
            chrome.storage.sync.get(['enableSslCheck'], (data) => {
                if (data.enableSslCheck !== false) {
                    checkSSL(details.tabId, details.url);
                }
            });
        }
    }
);

function checkSSL(tabId, url) {
    const urlObj = new URL(url);
    if (urlObj.protocol !== 'https:') {
        chrome.tabs.sendMessage(tabId, {action: "showSslWarning", message: "Небезопасное соединение (HTTP)"});
        return;
    }

    fetch(url, {method: 'HEAD'})
        .then(response => {
            const securityInfo = response.headers.get('Strict-Transport-Security');
            if (!securityInfo) {
                chrome.tabs.sendMessage(tabId, {action: "showSslWarning", message: "Отсутствует HSTS"});
            }
        })
        .catch(error => {
            chrome.tabs.sendMessage(tabId, {action: "showSslWarning", message: "Проблема с SSL-сертификатом"});
        });
}

function getCachedCheckResult(url) {
    return new Promise((resolve) => {
        chrome.storage.local.get(['urlCache'], (result) => {
            const cache = result.urlCache || {};
            const cachedResult = cache[url];
            if (cachedResult && Date.now() - cachedResult.timestamp < 24 * 60 * 60 * 1000) {
                resolve(cachedResult.result);
            } else {
                resolve(null);
            }
        });
    });
}

function setCachedCheckResult(url, result) {
    chrome.storage.local.get(['urlCache'], (data) => {
        const cache = data.urlCache || {};
        cache[url] = {result: result, timestamp: Date.now()};
        chrome.storage.local.set({urlCache: cache});
    });
}

function showBlockNotification(url) {
    chrome.notifications.create({
        type: 'basic',
        iconUrl: 'images/icon128.png',
        title: 'Сайт заблокирован',
        message: `AntiPhish Guard заблокировал доступ к ${url}`
    });
}

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === "reportSuspiciousSite") {
        chrome.storage.sync.get(['blacklist'], (result) => {
            const blacklist = result.blacklist || [];
            if (!blacklist.includes(request.url)) {
                blacklist.push(request.url);
                chrome.storage.sync.set({blacklist: blacklist}, () => {
                    sendResponse({status: "success"});
                });
            } else {
                sendResponse({status: "already_blacklisted"});
            }
        });
        return true;
    }
});

Функция «updatePhishingDatabase отвечает за обновление локальной базы данных фишинговых сайтов. Она вызывается при установке расширения и затем каждые 24 часа с помощью chrome.alarms API. Функция делает запрос к API, получает данные в формате JSON и сохраняет их в локальное хранилище Chrome. Это позволит нам иметь актуальную базу даже при отсутствии интернет-соединения. Я выбрал как основу Google Safe Browsing API (Safe Browsing – Google Safe Browsing), плюсом дополнительным шагом проверки сделал запрос к другому VirusTotal API. checkPhishing - основная функция проверки URL на фишинг. Она использует несколько методов для определения потенциальной угрозы: сначала проверяет кэшированный результат проверки. Если результат не найден в кэше, она выполняет проверку через Google Safe Browsing API, локальную базу данных, VirusTotal API (если эта опция включена в настройках) и анализ содержимого страницы (если эта опция также включена). В зависимости от результатов проверки, функция может заблокировать доступ к сайту, показать предупреждение или разрешить доступ. Функция checkUrlWithGoogleSafeBrowsing использует Google Safe Browsing API v4 для проверки URL на наличие угроз. Она отправляет POST-запрос с информацией о проверяемом URL и анализирует ответ. Если API возвращает пустой объект, это означает, что URL безопасен. Функция checkWithVirusTotal в свою очередь отправляет GET-запрос к API VirusTotal, передавая проверяемый URL. Если хотя бы один из антивирусных движков VirusTotal пометил URL как подозрительный, функция возвращает статус 'suspicious'. checkSSL проверяет, использует ли сайт HTTPS-протокол и поддерживает ли он HSTS (HTTP Strict Transport Security). Здесь немного лирики SSL-сертификаты - это важный элемент безопасности в современном интернете. Однако не все сертификаты одинаково надежны, и фишеры научились использовать бесплатные сертификаты для придания своим сайтам видимости легитимности. Наш SSL-checker выполняет следующие проверки: валидность сертификата - проверяет, не истек ли срок действия и выдан ли он доверенным центром сертификации; соответствие домену - проверяет, что сертификат выдан именно для того домена, который посещает пользователь; современные протоколы - проверяем, поддерживает ли сайт TLS 1.3 и отключены ли устаревшие небезопасные протоколы; HSTS - проверяет, использует ли сайт HTTP Strict Transport Security для защиты от атак типа SSL-stripping. Если SSL-checker обнаруживает проблемы, он не блокирует доступ к сайту автоматически (ведь это может быть легитимный, но плохо настроенный ресурс), и показывает пользователю предупреждение и советы по безопасности.
Если обнаружены проблемы с безопасностью соединения, функция отправляет сообщение в контентный скрипт для отображения предупреждения пользователю. getCachedCheckResult и setCachedCheckResult - эти функции позволяют сохранять результаты проверок URL в локальном хранилище Chrome и извлекать их оттуда. Кэширование результатов позволяет значительно ускорить работу расширения при повторных посещениях сайтов. Результаты хранятся в течение 24 часов, после чего считаются устаревшими и удаляются из кэша. Функция showBlockNotification использует chrome.notifications API для отображения всплывающего уведомления, информирующего пользователя о блокировке подозрительного сайта. Для получения API ключей перейдите https://console.cloud.google.com/apis/credentials и https://www.virustotal.com/gui/my-apikey, зарегистрируйтесь и получите ключи. Весь процесс получения ключей занимает не более 5-10 минут, поэтому не ленитесь. Свои ключи вставляйте вместо «GoogleApiKeys» и «VirusTotalAPIKeys». Если вам интересно взаимодействие с другими API (или вы уже работает и имеются ключи) можете аналогично внедрить.


А если фишинговый сайт настолько нов, что еще не попал в базы данных? Внедряем контент-анализатор content.js. Данный компонент будет выполнять глубокое сканирование содержимого страницы, занимаясь поиском признаков фишинговой активности. Вот некоторые из проверок, которые выполняет контент-анализатор:
  • Поиск форм ввода конфиденциальной информации (пароли, номера кредитных карт, SSN).
  • Анализ использования брендов и логотипов известных компаний.
  • Проверка несоответствий между доменным именем и контентом страницы.
  • Поиск подозрительных JavaScript-скриптов, которые могут использоваться для кражи данных.
  • Анализ структуры HTML на предмет признаков клонированных легитимных сайтов.
Чтобы не замедлять работу браузера, анализ контента выполняется в отдельном потоке, используя Web Workers. Это позволяет проводить сложные вычисления, не блокируя основной поток выполнения JavaScript.
Сначала выполняются быстрые, базовые проверки. Если они не выявляют ничего подозрительного, переходит к более глубокому анализу. Это позволяет быстро пропускать безопасные сайты и концентрировать вычислительные ресурсы на потенциально опасных.


Переходим непосредственно к реализации контентного скрипта, эта часть расширения будет внедряться непосредственно в веб-страницы. Он имеет доступ к DOM страницы и может анализировать её содержимое. В нашем случае, скрипт будет отвечать за анализ страницы на наличие подозрительных элементов и отображение предупреждений пользователю.

JavaScript: Скопировать в буфер обмена
Код:
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === "analyzeContent") {
        const suspicious = analyzePageContent();
        sendResponse({suspicious: suspicious});
    }
});

function analyzePageContent() {
    const suspiciousElements = [
        'input[type="password"]',
        'input[name="creditcard"]',
        'input[name="ssn"]'
    ];

    const hasSuspiciousElements = suspiciousElements.some(selector =>
        document.querySelector(selector) !== null
    );

    const knownLogos = ['paypal', 'google', 'facebook', 'amazon', 'apple'];
    const images = document.querySelectorAll('img');
    const hasKnownLogos = Array.from(images).some(img =>
        knownLogos.some(logo => img.src.toLowerCase().includes(logo) || img.alt.toLowerCase().includes(logo))
    );

    const domain = window.location.hostname;
    const content = document.body.innerText.toLowerCase();
    const mismatchedContent = knownLogos.some(logo =>
        content.includes(logo) && !domain.includes(logo)
    );

    return hasSuspiciousElements || hasKnownLogos || mismatchedContent;
}

Контентный скрипт играет важную роль в расширении, так как он позволяет анализировать содержимое страницы в режиме реального времени, что невозможно сделать только с помощью проверки URL.
chrome.runtime.onMessage.addListener принимает сообщения от background.js и реагирует на два типа сообщений: analyzeContent запускает анализ содержимого страницы и отправляет результат обратно, showSslWarning отображает предупреждение о проблемах с SSL-сертификатом. Функция analyzePageContent позволяет выполнить анализ содержимого страницы на наличие подозрительных элементов. В данном случае я сделал, чтобы она проверяла три основных аспекта: наличие полей ввода для конфиденциальной информации (пароли, номера кредитных карт, номера социального страхования); наличие логотипов известных компаний, которые часто подделываются фишерами, несоответствие между доменом сайта и упоминаемыми на нем брендами. Функция возвращает true, если обнаружен хотя бы один подозрительный элемент. showSslWarning создает и отображает предупреждение о проблемах с SSL-сертификатом сайта. Предупреждение отображается в виде желтой полосы в верхней части страницы.


Теперь давайте создадим пользовательский интерфейс для нашего расширения. Он будет отображаться при клике на иконку расширения в панели инструментов браузера.
Для popup.html копируем и вставляем код:

HTML: Скопировать в буфер обмена
Код:
<html>
<head>
  <meta charset="UTF-8">
  <title>AntiPhish Guard</title>
  <style>
    body { width: 300px; padding: 10px; font-family: Arial, sans-serif; }
    h1 { text-align: center; }
    button { width: 100%; margin-top: 10px; padding: 5px; }
  </style>
</head>
<body>
  <h1>AntiPhish Guard</h1>
  <div id="status">Статус: Активен</div>
  <div id="stats">
    Заблокировано сайтов: <span id="blocked-count">0</span><br>
    Предупреждений: <span id="warned-count">12</span>
  </div>
  <button id="whitelist">Добавить сайт в белый список</button>
  <button id="report">Сообщить о подозрительном сайте</button>
  <button id="sync">Синхронизировать списки</button>
  <script src="popup.js"></script>
</body>
</html>

Окно сделал с достаточно простым интерфейсом с информацией о статусе расширения, статистикой заблокированных сайтов и предупреждениями, а также с кнопками для управления белым и черным списками, синхронизации и экспорта/импорта данных.

Код для popup.js будет слегка побольше, но и функционал будет транслировать соответствующий:

JavaScript: Скопировать в буфер обмена
Код:
document.addEventListener('DOMContentLoaded', function() {
    updateStatistics();
    document.getElementById('whitelist').addEventListener('click', addToWhitelist);
    document.getElementById('report').addEventListener('click', reportSuspiciousSite);
    document.getElementById('sync').addEventListener('click', syncLists);
    document.getElementById('export').addEventListener('click', exportLists);
    document.getElementById('import').addEventListener('click', importLists);
});

function updateStatistics() {
    chrome.storage.local.get(['statistics'], (result) => {
        const stats = result.statistics || {blocked: 0, warned: 0};
        document.getElementById('blocked-count').textContent = stats.blocked;
        document.getElementById('warned-count').textContent = stats.warned;
    });
}

function addToWhitelist() {
    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
        const url = new URL(tabs[0].url);
        chrome.storage.sync.get(['whitelist'], (result) => {
            const whitelist = result.whitelist || [];
            if (!whitelist.includes(url.hostname)) {
                whitelist.push(url.hostname);
                chrome.storage.sync.set({whitelist: whitelist}, () => {
                    alert('Сайт добавлен в белый список');
                });
            } else {
                alert('Этот сайт уже в белом списке');
            }
        });
    });
}

function reportSuspiciousSite() {
    chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
        const url = tabs[0].url;
        chrome.runtime.sendMessage({action: "reportSuspiciousSite", url: url}, (response) => {
            if (response.status === "success") {
                alert('Спасибо за сообщение! Сайт добавлен в черный список.');
            } else if (response.status === "already_blacklisted") {
                alert('Этот сайт уже находится в черном списке.');
            } else {
                alert('Произошла ошибка при отправке сообщения. Пожалуйста, попробуйте позже.');
            }
        });
    });
}

function syncLists() {
    chrome.storage.sync.get(['whitelist', 'blacklist'], (data) => {
        alert('Синхронизация списков...\nБелый список: ' +
              JSON.stringify(data.whitelist) +
              '\nЧерный список: ' + JSON.stringify(data.blacklist));
    });
}

function exportLists() {
    chrome.storage.sync.get(['whitelist', 'blacklist'], function(data) {
        const blob = new Blob([JSON.stringify(data)], {type: 'application/json'});
        const url = URL.createObjectURL(blob);
        chrome.downloads.download({
            url: url,
            filename: 'antiphish_guard_lists.json'
        });
    });
}

function importLists() {
    const input = document.createElement('input');
    input.type = 'file';
    input.accept = '.json';
    input.onchange = function(event) {
        const file = event.target.files[0];
        const reader = new FileReader();
        reader.onload = function(e) {
            try {
                const data = JSON.parse(e.target.result);
                chrome.storage.sync.set(data, function() {
                    alert('Списки успешно импортированы');
                });
            } catch (error) {
                alert('Ошибка при импорте списков');
            }
        };
        reader.readAsText(file);
    };
    input.click();
}

setInterval(updateStatistics, 5000);

В нем updateStatistics обновляет отображаемую статистику, получая данные из локального хранилища Chrome, вызывается при загрузке popup и затем каждые 5 секунд для обеспечения актуальности данных (время можете выставить самостоятельно). addToWhitelist добавляет текущий сайт в белый список. Функция получает URL текущей вкладки, извлекает из него домен и добавляет его в белый список, хранящийся в синхронизированном хранилище Chrome. Сообщить пользователю о подозрительном сайте позволяет reportSuspiciousSite, она отправляет сообщение в background.js для добавления текущего сайта в черный список. Для синхронизации белого и черного списков я использовал syncLists, так как уже использовал эту функцию в других проектах, но здесь она просто выводит текущие списки, по хорошему здесь должна быть логика для синхронизации с сервером, можете самостоятельно вписать, если таковая имеется. exportLists экспортирует белый и черный списки в JSON-файл, который пользователь может сохранить на свой компьютер. importLists в свою очередь нужна для импорта списков из JSON-файла, открывает диалог выбора файла, читает его содержимое и обновляет списки в хранилище Chrome.


Следующий шаг - переходим к созданию страниц предупреждения и блокировки, которые будут показываться пользователю при обнаружении подозрительного или опасного сайта.
Начнем с warning.html, эта страница будет предупреждать пользователя о потенциальной опасности и предлагать два варианта действий: вернуться назад или продолжить:

HTML: Скопировать в буфер обмена
Код:
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <title>Предупреждение - AntiPhish Guard</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            background-color: #FFF8E1;
            padding: 20px;
        }
        h1 {
            color: #FF6F00;
        }
        .warning-text {
            margin: 20px 0;
        }
        .button {
            display: inline-block;
            padding: 10px 20px;
            margin: 10px;
            background-color: #FFA000;
            color: white;
            text-decoration: none;
            border-radius: 5px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <h1>⚠️ Предупреждение ⚠️</h1>
    <p class="warning-text">
        Этот сайт может быть потенциально опасным. AntiPhish Guard обнаружил подозрительные признаки.
        <br>
        Рекомендуем проявить осторожность и не вводить личные данные на этом сайте.
    </p>
    <button id="goBack" class="button">Вернуться назад</button>
    <button id="proceed" class="button">Продолжить на свой страх и риск</button>
    <script src="warning.js"></script>
</body>
</html>

Выглядит визуально:

Рисунок2.png



Для обработки действий пользователя наполняем warning.js:

JavaScript: Скопировать в буфер обмена
Код:
document.addEventListener('DOMContentLoaded', function() {
    const goBackButton = document.getElementById('goBack');
    const proceedButton = document.getElementById('proceed');

    goBackButton.addEventListener('click', function() {
        chrome.tabs.getCurrent(function(tab) {
            chrome.tabs.goBack(tab.id);
        });
    });

    proceedButton.addEventListener('click', function() {
        chrome.tabs.getCurrent(function(tab) {
            chrome.storage.local.get(['lastBlockedUrl'], function(result) {
                if (result.lastBlockedUrl) {
                    chrome.tabs.update(tab.id, {url: result.lastBlockedUrl});
                } else {
                    chrome.tabs.remove(tab.id);
                }
            });
        });
    });
});

Скрипт добавляет функциональность кнопкам на странице предупреждения. Кнопка "Вернуться назад" возвращает пользователя на предыдущую страницу, а кнопка "Продолжить на свой страх и риск" позволяет пользователю перейти на подозрительный сайт, несмотря на предупреждение.

Если будет обнаружен опасный сайт будет выскакивать следующая страница blocked.html, она будет информировать пользователя о блокировке опасного сайта и предлагать вернуться на предыдущую страницу:

HTML: Скопировать в буфер обмена
Код:
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Сайт заблокирован</title>
    <style>
        body { font-family: Arial, sans-serif; text-align: center; padding: 50px; background-color: #f8d7da; }
        h1 { color: #721c24; }
        p { color: #721c24; }
        button { background-color: #dc3545; color: white; border: none; padding: 10px 20px; cursor: pointer; }
    </style>
</head>
<body>
    <h1>⚠️ Внимание! Сайт заблокирован ⚠️</h1>
    <p>Этот сайт был определен как потенциально опасный и заблокирован расширением AntiPhish Guard.</p>
    <p>Рекомендуем немедленно покинуть эту страницу.</p>
    <button onclick="window.history.back()">Вернуться назад</button>
</body>
</html>

Выглядит визуально:

Рисунок3.png



Чтобы проверки выполнялись параллельно можно создать файл safeBrowsing.js и внести только функции проверки:

JavaScript: Скопировать в буфер обмена
Код:
export async function checkUrl(url) {
  const [googleResult, phishTankResult] = await Promise.all([
    checkUrlWithGoogleSafeBrowsing(url),
    checkUrlWithPhishTank(url)
  ]);

  return googleResult && phishTankResult;
}

async function checkUrlWithGoogleSafeBrowsing(url) {
  const apiKey = 'GoogleApiKeys';
  const apiUrl = `https://safebrowsing.googleapis.com/v4/threatMatches:find?key=${apiKey}`;

  const requestBody = {
    client: {
      clientId: "AntiPhishGuard",
      clientVersion: "1.0.0"
    },
    threatInfo: {
      threatTypes: ["MALWARE", "SOCIAL_ENGINEERING"],
      platformTypes: ["ANY_PLATFORM"],
      threatEntryTypes: ["URL"],
      threatEntries: [{ url: url }]
    }
  };

  try {
    const response = await fetch(apiUrl, {
      method: 'POST',
      body: JSON.stringify(requestBody)
    });
    const data = await response.json();
    return Object.keys(data).length === 0;
  } catch (error) {
    console.error('Error checking URL with Google Safe Browsing:', error);
    return true;
  }
}

async function checkWithVirusTotal(url) {
  const apiKey = 'VirusTotalAPIKeys';
  const apiUrl = `https://www.virustotal.com/vtapi/v2/url/report`;

  const requestBody = new FormData();
  requestBody.append('url', url);
  requestBody.append('format', 'json');
  requestBody.append('app_key', apiKey);

  try {
    const response = await fetch(apiUrl, {
      method: 'POST',
      body: requestBody
    });
    const data = await response.json();
    return data.results.valid === false;
  } catch (error) {
    console.error('Error checking URL:', error);
    return true;
  }
}

В основном скрипте я уже реализовал параллельную проверку, но если вам необходимо , можете реализовать систему фолбэка в этом файле. Если один API недоступен, попробуйте другой. Если все API недоступны, полагайтесь на локальную базу данных и эвристический анализ.


Файл contentAnalyzer.js предназначен для анализа содержимого веб-страниц в фоновом режиме, используя Web Worker. Это позволяет выполнять ресурсоемкие операции анализа без блокировки основного потока выполнения. Он будет включать в себя буквально пару строк кода: self.onmessage - обработчик сообщений, который принимает содержимое страницы для анализа; analyzeContent функция, которая выполняет фактический анализ содержимого страницы. Здесь должна быть реализована та же логика, что и в функции analyzePageContent из content.js. Использование Web Worker позволяет выполнять сложный анализ без ущерба для производительности основного скрипта расширения.

JavaScript: Скопировать в буфер обмена
Код:
self.onmessage = function(e) {
    const content = e.data;
    const result = analyzeContent(content);
    self.postMessage(result);
}

function analyzeContent(content) {
    const suspiciousElements = [
        'input[type="password"]',
        'input[name="creditcard"]',
        'input[name="ssn"]'
    ];

    const hasSuspiciousElements = suspiciousElements.some(selector =>
        document.querySelector(selector) !== null
    );

    const knownLogos = ['paypal', 'google', 'facebook', 'amazon', 'apple'];
    const images = document.querySelectorAll('img');
    const hasKnownLogos = Array.from(images).some(img =>
        knownLogos.some(logo => img.src.toLowerCase().includes(logo) || img.alt.toLowerCase().includes(logo))
    );

    const domain = window.location.hostname;
    const content = document.body.innerText.toLowerCase();
    const mismatchedContent = knownLogos.some(logo =>
        content.includes(logo) && !domain.includes(logo)
    );
    
    return suspicious;
}

И заключительным останется собрать страницу настроек расширения. Код позволяет пользователю настраивать различные параметры работы (включение/выключение анализа содержимого страницы, включение/выключение проверки SSL, включение/выключение использования VirusTotal API):

HTML: Скопировать в буфер обмена
Код:
<!DOCTYPE html>
<html>
<head>
    <title>Настройки AntiPhish Guard</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            padding: 20px;
        }
        label {
            display: block;
            margin-top: 10px;
        }
    </style>
</head>
<body>
    <h1>Настройки AntiPhish Guard</h1>
    <label>
        <input type="checkbox" id="enableContentAnalysis"> Включить анализ содержимого страницы
    </label>
    <label>
        <input type="checkbox" id="enableSslCheck"> Включить проверку SSL
    </label>
    <label>
        <input type="checkbox" id="enableVirusTotal"> Использовать VirusTotal API
    </label>
    <button id="save">Сохранить настройки</button>
    <script src="options.js"></script>
</body>
</html>

Файл options.js обеспечивает функциональность страницы настроек, позволяя сохранять и загружать пользовательские предпочтения. Основные функции: загрузка текущих настроек при открытии страницы и сохранение новых настроек при нажатии кнопки "Сохранить":

JavaScript: Скопировать в буфер обмена
Код:
document.addEventListener('DOMContentLoaded', function() {
    chrome.storage.sync.get(['enableContentAnalysis', 'enableSslCheck', 'enableVirusTotal'], function(items) {
        document.getElementById('enableContentAnalysis').checked = items.enableContentAnalysis !== false;
        document.getElementById('enableSslCheck').checked = items.enableSslCheck !== false;
        document.getElementById('enableVirusTotal').checked = items.enableVirusTotal !== false;
    });

    document.getElementById('save').addEventListener('click', function() {
        const enableContentAnalysis = document.getElementById('enableContentAnalysis').checked;
        const enableSslCheck = document.getElementById('enableSslCheck').checked;
        const enableVirusTotal = document.getElementById('enableVirusTotal').checked;

        chrome.storage.sync.set({
            enableContentAnalysis: enableContentAnalysis,
            enableSslCheck: enableSslCheck,
            enableVirusTotal: enableVirusTotal
        }, function() {
            alert('Настройки сохранены');
        });
    });
});

Скрипт использует chrome.storage.sync для хранения настроек, что позволяет синхронизировать их между разными устройствами пользователя.


Теперь, когда мы реализовали все основные компоненты нашего расширения, давайте протестируем его. Для этого мы создадим тестовую HTML-страницу, которая будет имитировать фишинговый сайт.
Откройте файл test_phishing.html и скопируйте код:

HTML: Скопировать в буфер обмена
Код:
<head>
    <title>Welcome to SecureBank</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            margin: 0;
            padding: 0;
            display: flex;
            flex-direction: column;
            align-items: center;
            min-height: 100vh;
            background-color: #f0f0f0;
        }
        h1 {
            color: #333;
            margin-top: 40px;
            margin-bottom: 30px;
        }
        .container {
            background-color: white;
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 0 10px rgba(0,0,0,0.1);
            width: 300px;
        }
        label {
            display: block;
            margin-top: 10px;
            text-align: left;
        }
        input {
            display: block;
            margin: 5px 0 15px;
            padding: 8px;
            width: 100%;
            box-sizing: border-box;
        }
        input[type="submit"] {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px;
            cursor: pointer;
            margin-top: 20px;
        }
        img {
            max-width: 200px;
            margin-top: 30px;
        }
    </style>
</head>
<body>
    <h1>Welcome to SecureBank</h1>
    <div class="container">
        <form>
            <label for="username">Username:</label>
            <input type="text" id="username" name="username">
            
            <label for="password">Password:</label>
            <input type="password" id="password" name="password">
            
            <label for="creditcard">Credit Card:</label>
            <input type="text" id="creditcard" name="creditcard">
            
            <input type="submit" value="Login">
        </form>
    </div>
    <img src="C:\Users\user\Downloads\Без названия.png">
</body>
</html>

Лого можете подгрузить любого банка или платежной системы. Эта тестовая страница содержит несколько элементов, которые должны вызвать подозрение у нашего расширения:
  • Поле ввода пароля
  • Поле ввода номера кредитной карты
  • Изображение с логотипом PayPal

Рисунок4.png



Давайте протестируем расширения:
  • Откройте браузер Chrome и перейдите на страницу chrome://extensions/.
  • Включите "Режим разработчика" (Developer mode) в правом верхнем углу.
  • Нажмите "Загрузить распакованное расширение" (Load unpacked) и выберите папку с вашим расширением.
  • Убедитесь, что расширение успешно загрузилось и его иконка появилась в панели инструментов Chrome.

Рисунок 5.png



  • Откройте файл test_phishing.html в браузере.
  • Расширение должно обнаружить подозрительные элементы на странице и показать предупреждение.
  • Попробуйте нажать кнопку "Вернуться назад" и "Продолжить на свой страх и риск", чтобы убедиться, что они работают корректно.
  • Откройте popup расширения, кликнув на его иконку, и проверьте, что статистика обновляется корректно.

рисунок6.png



  • Попробуйте добавить текущий URL в белый список и убедитесь, что предупреждение больше не появляется при повторном открытии страницы.
  • Сообщите о подозрительном сайте через popup и проверьте, что URL добавляется в черный список.
  • Попробуйте открыть несколько безопасных сайтов (например, google.com, github.com) и убедитесь, что расширение не блокирует их.
  • Если у вас есть доступ к реальному фишинговому URL (будьте осторожны!), попробуйте открыть его и убедитесь, что расширение блокирует доступ.
Мы соорудили с вами мощное оружие - расширение для защиты от фишинга, которое использует различные методы для обнаружения потенциально опасных сайтов. И это расширение демонстрирует, как можно использовать возможности браузерных API для создания эффективных инструментов безопасности. Мы реализовали асинхронные проверки, кэширование результатов и оптимизацию производительности с использованием различных техник. Важно отметить, что борьба с фишингом - это постоянно развивающаяся область, атакующие постоянно изобретают новые способы обмана пользователей. Поэтому критически важно регулярно обновлять базы данных фишинговых сайтов и алгоритмы обнаружения (даже для самих атакующих). Помните, что даже самое совершенное техническое решение не может заменить бдительность пользователя. Надеюсь, этот tutorial был полезен и вдохновил вас на создание собственных инструментов кибербезопасности. Помните: в мире хакинга учеба никогда не заканчивается. Продолжайте исследовать, экспериментировать и, главное, всегда используйте свои навыки этично и ответственно! Хочется обратной связи от форумчан - как выглядит идельное расширение такого формата для вас? Понравилась ли вам статья? Какой функционал вы бы сами добавили в расширение?

Patr1ck специально для XSS.IS.
 
Сверху Снизу