Pull to refresh

Heartbleed на Rust

Reading time 3 min
Views 13K
Original author: tedu
В комментариях к одной из ссылок на 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 <fcntl.h>
#include <unistd.h>
#include <assert.h>

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 — сочиться, испускать

Ссылки

Tags:
Hubs:
+23
Comments 10
Comments Comments 10

Articles