Уязвимость в парадигме функционального программирования? - PullRequest
15 голосов
/ 31 января 2009

Несколько дней назад было несколько вопросов об уязвимостях переполнения буфера (таких как Есть ли в Java переполнения буфера? , Secure C и университеты - обучены переполнению буфера назвать пару), что может произойти в императивных языках программирования, таких как C.

В функциональном программировании (из очень ограниченного опыта, который я испытал Haskell ), я вижу, как не возникает таких уязвимостей, как переполнение буфера, потому что эти проблемы являются результатом изменения состояние программы или области памяти. (Пожалуйста, поправьте меня, если я ошибаюсь.)

Без учета возможности уязвимостей, присутствующих в компиляторе, интерпретаторе или среде выполнения, Существуют ли какие-либо уязвимости безопасности, которые существуют в парадигме функционального программирования? Существуют ли какие-либо конкретные типы уязвимостей, которые существуют в функциональном программировании, но отсутствуют в императивном программировании?

Ответы [ 4 ]

15 голосов
/ 31 января 2009

Если программист не ожидает, что [некоторый ввод] может заставить [program] потреблять больше, чем доступные ресурсы, это уязвимость в виде возможного DoS. Это слабость всех языков, полных тьюринга, которые я видел, но лень Хаскелла затрудняет рассуждения о том, что включает в себя вычисление.

В качестве (довольно надуманного) примера,

import Control.Monad (when)
import System (getArgs)
main = do
    files <- getArgs
    contents <- mapM readFile files
    flip mapM_ (zip files contents) $ \(file, content) ->
        when (null content) $ putStrLn $ file ++ " is empty"

Наивный программист может подумать: «Haskell ленив, поэтому он не будет открывать и читать файлы, пока в этом нет необходимости», и «Haskell собирает мусор, поэтому, как только он будет сделан с файлом, он может закрыть файл справиться". К сожалению, эта программа на самом деле просто откроет сразу много файлов (зависящих от реализации), и только пустые файлы закроют свои файловые дескрипторы (побочный эффект правил реализации реализации):

$ ghc --make -O2 Test
[1 of 1] Compiling Main             ( Test.hs, Test.o )
Linking Test ...
$ strace -etrace=open,close ./Test dir/* /dev/null
...
open("dir/1", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 3
open("dir/2", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 4
open("dir/3", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 5
open("dir/4", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 6
open("dir/5", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 7
...
open("/dev/null", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_LARGEFILE) = 255
close(255)
/dev/null is empty
$

Возможно, вы не ожидали ошибки -EMFILE "Too many open files".

Как я уже сказал, это надуманный пример, который может встречаться и на других языках, но в Haskell проще пропустить использование некоторых ресурсов.

5 голосов
/ 31 января 2009

Функциональные языки имеют недооцененное преимущество «безопасность через неизвестность» из-за их моделей исполнения. Если вы посмотрите на эксплойты безопасности в программах на C, они используют преимущества слабой системы типов, манипуляции с указателями и отсутствие проверки границ, но, что более важно, они используют хорошо понятную, прямую модель исполнения. Например, вы можете надежно разбить стек в C, потому что относительно легко узнать, где находится стек , просто взяв адрес локальных переменных. Многие другие эксплойты опираются на аналогичное низкоуровневое понимание модели исполнения.

Напротив, не так очевидно, как функциональный код будет скомпилирован в двоичный файл, поэтому не так просто разработать рецепт выполнения внедренного кода или доступа к привилегированным данным. По иронии судьбы, незаметность моделей исполнения обычно считается слабостью функциональных языков, поскольку программисты не всегда хорошо понимают, как будет работать их код.

4 голосов
/ 31 января 2009

Я так не думаю.

На мой взгляд, вместо парадигмы программирования уязвимости, такие как переполнение буфера, больше связаны с архитектурой компилятора / интерпретатора / виртуальной машиной.

Например, если вы используете функциональное программирование в среде .NET (C #, VB и т. Д.), Если вы имеете дело с управляемыми объектами, будут переполнены буферы.

1 голос
/ 31 января 2009

Возможно, легче вызвать неограниченную рекурсию (и полученное в результате использование памяти) в функциональной реализации, чем в соответствующей императивной реализации. В то время как императивная программа может просто зайти в бесконечный цикл при обработке искаженных входных данных, функциональная программа может вместо этого поглотить всю память, которую она может, делая ту же обработку. Как правило, ответственность за ограничение объема памяти, которую может потреблять конкретный процесс, лежит на виртуальной машине, на которой выполняется программа. (Даже для собственных процессов Unix ulimit может обеспечить эту защиту.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...