Embedded

Безопасность CAN: уязвимости шины и меры защиты

Введение

CAN-протокол был спроектирован в 1980-х годах для замкнутых, физически изолированных автомобильных сетей без какого-либо предположения о потенциально злонамеренных участниках шины — современные автомобили с многочисленными точками сетевого подключения (Bluetooth, Wi-Fi, сотовая связь) делают эти изначальные архитектурные допущения проблематичными с точки зрения безопасности. В статье разберём основные уязвимости классического CAN и практичные меры защиты, применимые при разработке систем, взаимодействующих с CAN-шиной.

Концепция

CAN не имеет встроенной аутентификации отправителя — любой узел, физически подключённый к шине, может отправить кадр с произвольным идентификатором, выдавая себя за любой другой узел системы (spoofing), и протокол сам по себе не предоставляет средств для обнаружения такой подмены. Отсутствие шифрования делает все передаваемые по шине данные доступными для перехвата любым устройством, подключённым к той же физической шине. Атака отказа в обслуживании (DoS) на уровне CAN относительно проста технически — узел, постоянно отправляющий кадры с наивысшим приоритетом (идентификатор 0x000), способен фактически парализовать шину, поскольку механизм арбитража гарантирует таким кадрам приоритет над всеми остальными.

Пример кода: Безопасность CAN

// Демонстрационная (для понимания угрозы, НЕ для практического использования
// без явного разрешения на тестирование собственной, контролируемой системы) —
// иллюстрация того, как просто технически выдать себя за другой узел шины
void demonstrateSpoofingRisk(QCanBusDevice *device)
{
    // Узел может отправить кадр с ЛЮБЫМ идентификатором, включая принадлежащий
    // совершенно другому, легитимному узлу системы — протокол это не предотвращает
    QCanBusFrame spoofedFrame;
    spoofedFrame.setFrameId(0x0C0); // идентификатор, обычно используемый ECU двигателя
    spoofedFrame.setPayload(QByteArray::fromHex("FFFF")); // произвольные, потенциально ложные данные

    device->writeFrame(spoofedFrame); // протокол CAN сам по себе не отличит это от легитимного сообщения
}
// Практичная мера защиты на уровне приложения: проверка правдоподобности (plausibility check)
// значений вместо слепого доверия любому полученному сообщению с ожидаемым идентификатором
bool isEngineRpmPlausible(double rpm, double previousRpm, double elapsedSeconds)
{
    const double MAX_RPM = 8000.0;
    const double MAX_RPM_CHANGE_PER_SECOND = 3000.0; // физически разумный предел скорости изменения

    if (rpm < 0 || rpm > MAX_RPM) {
        return false; // значение за пределами физически возможного диапазона
    }

    double changeRate = std::abs(rpm - previousRpm) / elapsedSeconds;
    if (changeRate > MAX_RPM_CHANGE_PER_SECOND) {
        return false; // нереалистично резкое изменение — возможный признак поддельного сообщения
    }

    return true;
}
// Использование в обработчике принятых сообщений — отклонение неправдоподобных значений
void handleEngineRpmFrame(double rpm, double &previousRpm, qint64 &lastTimestamp)
{
    qint64 now = QDateTime::currentMSecsSinceEpoch();
    double elapsedSeconds = (now - lastTimestamp) / 1000.0;

    if (isEngineRpmPlausible(rpm, previousRpm, elapsedSeconds)) {
        previousRpm = rpm;
        lastTimestamp = now;
        updateDisplayedRpm(rpm);
    } else {
        qWarning() << "Получено неправдоподобное значение оборотов:" << rpm << "— отброшено";
    }
}

Пояснения к коду

Демонстрационный пример demonstrateSpoofingRisk() (явно отмеченный как иллюстративный, не для практического злонамеренного использования) показывает фундаментальное архитектурное ограничение CAN — протокол физически не способен ограничить, какие идентификаторы может отправлять конкретный подключённый узел, в отличие от более современных протоколов с криптографической аутентификацией сообщений. isEngineRpmPlausible() показывает практичную, доступную на уровне приложения меру частичной защиты — без возможности криптографически подтвердить подлинность отправителя, приложение всё же может отклонять физически неправдоподобные значения (резкий скачок оборотов за невозможно короткое время может означать как реальную аппаратную проблему датчика, так и попытку внедрения поддельного сообщения), что не заменяет полноценную защиту, но снижает практическую эффективность простейших атак подмены.

Подводные камни

  • Полное доверие любому полученному CAN-сообщению с ожидаемым идентификатором без какой-либо дополнительной проверки правдоподобности значений — учитывая фундаментальное отсутствие аутентификации в самом протоколе CAN, приложения, обрабатывающие данные с шины (особенно в контексте безопасности, например, отображение критичных показаний водителю), не должны слепо доверять полученным значениям просто на основании совпадения идентификатора сообщения, что демонстрирует важность дополнительных мер вроде проверки правдоподобности из примера.
  • Недооценка серьёзности физического доступа к CAN-шине — поскольку основная защита классического CAN фактически сводится к физической изолированности шины (никто посторонний не может физически подключиться к проводам), любая дополнительная точка физического или сетевого доступа к CAN-шине автомобиля (диагностический разъём OBD-II, статья 365; беспроводные модули, подключённые к той же шине) потенциально открывает возможность для атак, и проектирование системы должно явно учитывать все реальные физические и сетевые точки доступа к шине, а не предполагать её абсолютную изолированность по умолчанию.
  • Реализация мер защиты только на уровне получающего приложения без рассмотрения более комплексных решений уровня шины (CAN с шифрованием — нестандартное расширение, требующее поддержки на всех узлах; брандмауэры на уровне шины — gateway-устройства, фильтрующие сообщения между разными сегментами CAN-сети современного автомобиля) — для действительно production-критичных систем (а не учебных или исследовательских проектов) мер защиты, реализуемых только в одном Qt-приложении, читающем шину, обычно недостаточно, и комплексная безопасность современных автомобильных сетей требует архитектурных решений на уровне всей системы, выходящих далеко за рамки одного клиентского приложения.
  • Применение знаний об уязвимостях CAN для тестирования или экспериментов на реальном, не принадлежащем явно тестировщику автомобиле, или без явного разрешения на тестирование безопасности конкретной системы — эксперименты с подменой сообщений, демонстрацией DoS-атак на шину, или иные практические тесты уязвимостей должны проводиться исключительно на собственном, контролируемом тестовом оборудовании (например, виртуальной шине из статьи 367) или в рамках явно авторизованного, легального тестирования безопасности (security research/pentesting с соответствующими разрешениями), а не на реальных, эксплуатируемых третьими лицами системах.