Разберите следующий фрагмент C: char buf[8]; strcpy(buf, "This is a long string"); — какие типы уязвимостей здесь присутствуют и как их устранить на уровне кода и на уровне проектных решений
Коротко: здесь — классический выход за пределы буфера (buffer overflow) из‑за использования небезопасной функции `strcpy`. Последствия: перепись памяти на стеке (stack-based buffer overflow), возможное нарушение целостности данных, крах программы, выполнение произвольного кода или утечка информации (в зависимости от контекста). Почему это происходит - Буфер: buf size=8\text{buf size} = 8buf size=8. - Строка `"This is a long string"` занимает string length=21\text{string length} = 21string length=21 символов + терминатор `\0` = 22 \ 22\ 22 байта. - Переполнение: overflow bytes=22−8=14\text{overflow bytes} = 22 - 8 = 14overflow bytes=22−8=14. Типы уязвимостей - Stack-based buffer overflow (перезапись смежной стековой памяти, return address и т.д.). - Отсутствие проверки границ / небезопасные API (`strcpy`). - По сценарию — возможность ROP/кодовой инъекции, утечка данных (если смещаются указатели), DoS (крах программы). Устранение на уровне кода (практические варианты) 1. Использовать ограничивающие функции и проверять длину: - POSIX/BSD: - `strlcpy(buf, src, sizeof(buf));` — копирует с обрезкой, проверяйте возврат (если возврат >= sizeof(buf) — усечение). - Портируемо: - `snprintf(buf, sizeof buf, "%s", src);` — безопасно по границам. - C11 Annex K / MS: - `strncpy_s` / `memcpy_s` с проверкой результата (там, где поддерживается). 2. Выделять динамически нужный размер: - `char *buf = malloc(strlen(src) + 1); if (!buf) ...; strcpy(buf, src);` - Или `asprintf(&buf, "%s", src);` (если доступно). 3. Явно проверять длину перед копированием: - `if (strlen(src) + 1 > sizeof buf) /* обработать ошибку или усечь */;` 4. Предпочитать безопасные абстракции (в C++ — `std::string`). 5. Пример безопасного исправления: - `char buf[8]; const char *src = "This is a long string"; snprintf(buf, sizeof buf, "%s", src); /* проверить усечение */` Устранение на уровне проектных решений и окружения - Компилятор и линковка: - Включить стековые защиты: `-fstack-protector-strong` / `-fstack-protector-all`. - Включить защиту времени выполнения: `-D_FORTIFY_SOURCE=2`. - Включить оптимизации проверки: `-Wstringop-overflow`, `-Wformat-security`. - Runtime/OS: - Включить ASLR, NX (DEP), и, где возможно, Control-Flow Integrity (CFI). - Тестирование и анализ: - Статический анализ (Coverity, clang-tidy, cppcheck). - Динамическое тестирование: AddressSanitizer (`-fsanitize=address`), UBSan, fuzzing (libFuzzer, AFL). - Политики и процессы: - Код-ревью, стандарты безопасного кодирования (CERT C, MISRA), автотесты на проверку границ. - По возможности переход на языки с управляемой памятью (Rust, Go) или использование проверенных библиотек для строк. - Архитектурные меры: - Минимизировать размер привилегий, разделять компоненты, использовать sandboxing для сниженного ущерба при компрометации. Итог: правильный код — либо гарантированная проверка длины и использование ограничивающих функций (или динамическое выделение), либо переход на безопасные абстракции; проектно — применять компиляторные и системные защиты, статический/динамический анализ и безопасные практики разработки.
Почему это происходит
- Буфер: buf size=8\text{buf size} = 8buf size=8.
- Строка `"This is a long string"` занимает string length=21\text{string length} = 21string length=21 символов + терминатор `\0` = 22 \ 22\ 22 байта.
- Переполнение: overflow bytes=22−8=14\text{overflow bytes} = 22 - 8 = 14overflow bytes=22−8=14.
Типы уязвимостей
- Stack-based buffer overflow (перезапись смежной стековой памяти, return address и т.д.).
- Отсутствие проверки границ / небезопасные API (`strcpy`).
- По сценарию — возможность ROP/кодовой инъекции, утечка данных (если смещаются указатели), DoS (крах программы).
Устранение на уровне кода (практические варианты)
1. Использовать ограничивающие функции и проверять длину:
- POSIX/BSD:
- `strlcpy(buf, src, sizeof(buf));` — копирует с обрезкой, проверяйте возврат (если возврат >= sizeof(buf) — усечение).
- Портируемо:
- `snprintf(buf, sizeof buf, "%s", src);` — безопасно по границам.
- C11 Annex K / MS:
- `strncpy_s` / `memcpy_s` с проверкой результата (там, где поддерживается).
2. Выделять динамически нужный размер:
- `char *buf = malloc(strlen(src) + 1); if (!buf) ...; strcpy(buf, src);`
- Или `asprintf(&buf, "%s", src);` (если доступно).
3. Явно проверять длину перед копированием:
- `if (strlen(src) + 1 > sizeof buf) /* обработать ошибку или усечь */;`
4. Предпочитать безопасные абстракции (в C++ — `std::string`).
5. Пример безопасного исправления:
- `char buf[8]; const char *src = "This is a long string"; snprintf(buf, sizeof buf, "%s", src); /* проверить усечение */`
Устранение на уровне проектных решений и окружения
- Компилятор и линковка:
- Включить стековые защиты: `-fstack-protector-strong` / `-fstack-protector-all`.
- Включить защиту времени выполнения: `-D_FORTIFY_SOURCE=2`.
- Включить оптимизации проверки: `-Wstringop-overflow`, `-Wformat-security`.
- Runtime/OS:
- Включить ASLR, NX (DEP), и, где возможно, Control-Flow Integrity (CFI).
- Тестирование и анализ:
- Статический анализ (Coverity, clang-tidy, cppcheck).
- Динамическое тестирование: AddressSanitizer (`-fsanitize=address`), UBSan, fuzzing (libFuzzer, AFL).
- Политики и процессы:
- Код-ревью, стандарты безопасного кодирования (CERT C, MISRA), автотесты на проверку границ.
- По возможности переход на языки с управляемой памятью (Rust, Go) или использование проверенных библиотек для строк.
- Архитектурные меры:
- Минимизировать размер привилегий, разделять компоненты, использовать sandboxing для сниженного ущерба при компрометации.
Итог: правильный код — либо гарантированная проверка длины и использование ограничивающих функций (или динамическое выделение), либо переход на безопасные абстракции; проектно — применять компиляторные и системные защиты, статический/динамический анализ и безопасные практики разработки.