[Перевод] Heartbleed на Rust
В комментариях к одной из ссылок на Hacker News некто утверждал, что использование Rust предотвратило бы Heartlbeed, что код бы даже не скомпилировался. Это прозвучало как вызов! Тред начинается вот здесь. Я не собирался ни к кому придираться, но утверждение о предотвращении Heartbleed оказалось удачно сформулировано. В отличие от расплывчатых заявлений о безопасности работы с памятью в целом, конкретно данное утверждение можно протестировать.Я не планирую реализовать весь стек TLS на Rust, поэтому срежу путь и уменьшу масштаб проблемы. Надеюсь, что моя модель сохранит суть проблемы. В двух словах, цель такова: написать программу, которая читает файл (пакет) из файловой системы (сети) и отправляет его обратно (этакий сетевой вариант echo). Длина echo-запроса будет закодирована одним байтом, за которым следуют данные. Это эквивалентно уязвимости TLS. Наша программа будет принимать пару таких пакетов, yourping и myping, и отвечать пакетами yourecho и myecho. Если какие-либо данные из пакета your просочатся в пакет my, у нас проблема: heartbleed1.
Начнём с простой программы на Rust.
use std: old_io: File;
fn pingback (path: Path, outpath: Path, buffer: &mut[u8]) { let mut fd = File: open (&path); match fd.read (buffer) { Err (what) => panic!(«say {}», what), Ok (x) => if x < 1 { return; } } let len = buffer[0] as usize; let mut outfd = File::create(&outpath); match outfd.write_all(&buffer[0 .. len]) { Err(what) => panic!(«say {}», what), Ok (_) => () } }
fn main () { let buffer = &mut[0u8; 256]; pingback (Path: new («yourping»), Path: new («yourecho»), buffer); pingback (Path: new («myping»), Path: new («myecho»), buffer); } Программа компилируется, хотя и с предупреждениями из-за ламерского использования std: old_io. Не бог весть какой код, но и не самый ужасный. К примеру, мне удалось не использовать небезопасные межъязыковые интерфейсы (FFI) для вызова memcpy из C.Давайте посмотрим, что программа делает с простыми входными данными.
$ echo \#i have many secrets. this is one. > yourping $ echo \#i know your > myping $ ./bleed $ cat yourecho #i have many secrets. this is one. $ cat myecho #i know your secrets. this is one. Бинго! Секретные данные утекли.Конечно же, настоящий программист на Rust никогда не напишет подобной программы, поэтому, вероятно, я ещё и не продемонстрировал Heartbleed на Rust.
Давайте отдохнём от Rust и рассмотрим эквивалентный код на C.
#include
void pingback (char *path, char *outpath, unsigned char *buffer) { int fd; if ((fd = open (path, O_RDONLY)) == -1) assert (! «open»); if (read (fd, buffer, 256) < 1) assert(!"read"); close(fd); size_t len = buffer[0]; if ((fd = creat(outpath, 0644)) == -1) assert(!"creat"); if (write(fd, buffer, len) != len) assert(!"write"); close(fd); }
int main (int argc, char **argv) { unsigned char buffer[256]; pingback («yourping», «yourecho», buffer); pingback («myping», «myecho», buffer); } Анкетирование показало, что ни один настоящий программист на C никогда не напишет такой программы. Что же мы имеем? код, который не напишет ни один настоящий программист на C: heartbleedкод, который не напишет ни один настоящий программист на Rust: (задачка для читателя)
Смысл поста не в порицании Rust. Я мог бы написать похожую программу на Go, или даже на Haskell, если бы я был достаточно умён для понимания буррито. Смысл в том, что пока мы не поймём, что представляют собой уязвимости наподобие Heartbleed, мы едва ли сможем избежать их простым переключением на волшебный уязвимостойкий язык. Да, каждый слышал о Heartbleed, но это не обязательно делает его хорошим примером.
Возможно, аргумент о Heartbleed использовался не как отсылка к самому Heartbleed, а к пачке других больших и страшных проблем. Не уверен, что это делает аргумент лучше. «Уязвимости, подобные Heartbleed, но не слишком похожие» — плохо определённый класс проблем. Сложно оценить какие-либо утверждения о таком классе.
Говоря об уязвимостях и их разрешении, нам нужно быть точными и осторожными. Поднятый вокруг Heartbleed (Shellshock, и т.п.) хайп делает его привлекательной целью для построения аргументов, но стоит проверять сочетаемость примера и аргумента. Ошибочные примеры приводят к ошибочным решениям.
Примечания 1. bleed — сочиться, испускатьСсылки