Share This
Связаться со мной
Крути в низ
Categories
//Ловушки безопасности Ruby и как их избежать

Ловушки безопасности Ruby и как их избежать

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

lovushki bezopasnosti ruby i kak ih izbezhat 77c4790 - Ловушки безопасности Ruby и как их избежать

Перевод публикуется с сокращениями, автор оригинальной статьи Dhruv.

Ruby – универсальный язык, сочетающий в себе простоту синтаксиса и мощные функции. Благодаря популярности фреймворка Rails, Ruby входит в десятку лучших современных языков. Однако с таким комьюнити разработчиков существует множество распространенных ошибок, ведущих к серьезным последствиям.

Острые ножи

lovushki bezopasnosti ruby i kak ih izbezhat 320d13c - Ловушки безопасности Ruby и как их избежать

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

«Острые ножи» – это все доступное в Ruby метапрограммирование, и тот нюанс, что все открыто для изменения/переопределения в любой момент. Дальше станет понятнее. Рассмотрим ошибки и их решения.

Небезопасная десериализация

Выполнять все, что пользователь собирался отправлять – плохая мысль. А вот сериализация/десериализация пользовательского ввода не кажется такой уж плохой идеей, т. к. никакой код при этом не выполняется.

Небезопасная десериализация входит в первую десятку OWASP Top Ten чеклиста для веб-безопасности.

Основанная на Psych встроенная библиотека YAML Ruby поддерживает сериализацию пользовательских типов данных в YAML и обратно:

         # serialize.rb require 'yaml' require 'set'  s = Set.new([1, 2, 3]) File.open('set.yml', 'w') do |file|   YAML.dump(s, file) end     
         --- !ruby/object:Set hash:   1: true   2: true   3: true     

Десериализация этого YAML возвращает исходный тип данных:

         # deserialize.rb require 'yaml' require 'set'  file = File.read('set.yml') s = YAML.load(file) p s     
         $ ruby deserialize.rb  #<Set: {1, 2, 3}>     

Как вы можете видеть, строка -- !ruby/object: Set описывает, как повторно создавать экземпляры объектов из их текстовых представлений. Это открывает множество векторов атаки, которые могут перерасти в RCE.

Решение

Решение заключается в использовании безопасной загрузки. Используйте функцию YAML::safe_load вместо YAML::load. Она полностью блокирует загрузку пользовательских классов:

         # deserialize.rb require 'yaml' require 'set'  file = File.read('set.yml') s = YAML.safe_load(file) p s     
         $ ruby deserialize.rb  /usr/ruby/2.7.0/psych/class_loader.rb:97:in `find': Tried to load unspecified class: Set (Psych::DisallowedClass)     

Стандартные типы вроде хэшей и массивов все еще могут быть сериализованы в документы YAML и десериализованы из них, как и раньше.

         --- hash:   1: true   2: true   3: true     
         $ ruby deserialize.rb  {"hash"=>{1=>true, 2=>true, 3=>true}}     

IO hijacking

lovushki bezopasnosti ruby i kak ih izbezhat 4da4bb5 - Ловушки безопасности Ruby и как их избежать

Если вашему приложению необходимо читать или записывать пользовательские файлы с диска или делать запросы через API к пользовательским URL-адресам, вы можете написать следующий код:

         # test.rb puts 'Enter file path:' path = gets.chomp puts 'File contents:' open(path, 'r') do |file|   until file.eof? do     puts file.gets   end end     

У пользователя запрашивается имя файла, а потом его содержимое выводится построчно. Здесь есть ошибка.

Функция Kernel::open из приведенного выше фрагмента кода имеет еще одну дополнительную функцию, которая используется для порождения процессов. Посмотрите, что происходит, когда вы передаете начинающиеся с символа конвейера ( | ) входные данные:

         $ ruby test.rb  Enter file path: |date File contents: Sun Nov 15 22:03:33 IST 2020     

Она выполняет команду date и передает выходные данные обратно Ruby. Злоумышленник будет выполнять команды гораздо более разрушительные, чем в этом коде. В нашем примере базовое RCE – удаленный пользователь, выполняющий код на сервере с полными привилегиями, доступными вашему веб-приложению.

Никогда не запускайте код от ненадежного источника, такого как пользователь, но если все-таки придется – ограничьте доступ к выбранным ресурсам.

Решение

Никогда не используйте Kernel::open! Альтернативой является File::open, URL::open или IO::open, в зависимости от контекста. Попробуем еще раз, на этот раз с File::open вместо Kernel::open. Такой вариант гораздо более безопасен, поскольку он не дает доступа к shell:

         # test.rb puts 'Enter file path:' path = gets.chomp puts 'File contents:' File.open(path, 'r') do |file|   until file.eof? do     puts file.gets   end end     
         $ ruby test.rb  Enter file path:  |date File contents: Traceback (most recent call last): 	2: from test.rb:4:in `<main>' 	1: from test.rb:4:in `open' test.rb:4:in `initialize': No such file or directory @ rb_sysopen - |date (Errno::ENOENT)     

SQL injection

lovushki bezopasnosti ruby i kak ih izbezhat 24a42ec - Ловушки безопасности Ruby и как их избежать

Инъекция происходит, когда пользовательский ввод из фронтенда без проверок и очистки применяется непосредственно в SQL-запросе на бекенде.

Допустим, у вас есть столбец users, в котором выполняется поиск по введенному в форму имени. Вот, как бы это выглядело в Ruby:

         User.where("name = '#{name}'")     

Это небезопасно. Рассмотрим следующий ввод от злоумышленника:

         name = " ' OR '1' = '1" # Ввод от злоумышленника     

Запрос ложный, как видно в первой строке и крашится из-за вредоносного кода, как показано во второй строке. WHERE всегда true и возвращает каждого пользователя из таблицы:

         SELECT * FROM users WHERE name = '<name>'         -- <name> ложный запрос SELECT * FROM users WHERE name = ' ' OR '1' = '1' -- все сломалось     

Решение

Используйте один из следующих двух способов:

         User.where(["name = ?", name]) User.where({ name: name })     

Оба подхода предназначены для очистки значения перед генерацией запроса и поэтому невосприимчивы для этой атаки.

Автоинкремент ID

Когда вы создаете модель Rails, автоматически создается id – автоинкрементное целочисленное поле. По большей части этого достаточно. Целочисленные идентификаторы имеют преимущество: поскольку они постоянно увеличиваются, нет никаких шансов на коллизию. Что произойдет, если пострадает безопасность:

  • Масштабирование может стать сложнее. Если множество серверов работают независимо друг от друга, вполне вероятно, что они могут назначить один и тот же ID разным ресурсам.
  • Станет возможным определить, сколько у вас объектов. Можно зарегистрироваться и увидеть собственный идентификатор, чтобы узнать количество клиентов и даже оценить бизнес-показатели.
  • Можно будет перебирать и красть данные. Злоумышленник сможет вывести весь список и получить все ограниченные ресурсы.
  • Возможна утечка конфиденциальной информации. Незарегистрированные ресурсы, видимые только держателям ссылок, сломались бы из-за целочисленных ID.

Решение

Используйте UUID. С Rails сделать это будет очень просто. После несложного изменения все ваши модели будут использовать UUID в качестве первичных ключей:

         # config/application.rb  config.generators do |g|   g.orm :active_record, primary_key_type: :uuid end     

Если вы все-таки хотите использовать целые числа, берите большие, выбранные случайным образом. Посмотрим на структуру URL-адресов ролика из YouTube. Так выглядит его URL-адрес:

         https://www.youtube.com/watch?v=dQw4w9WgXcQ     

Идентификатор видео – это случайное число между $0$ и $64^{11}$ (более 73 квинтиллионов чисел), закодированное в Base64. Не совсем UUID, но и не целочисленное поле с автоинкрементом.

Инструменты в помощь

Ключевой вывод из описанных выше примеров – никогда не доверять юзеру. Данные пользователя не должны быть напрямую сериализованы/десериализованы, оценены или обработаны. В качестве дополнительных мер предосторожности подойдет специальный софт.

Статические анализаторы кода

lovushki bezopasnosti ruby i kak ih izbezhat d477efa - Ловушки безопасности Ruby и как их избежать

Инструменты анализа кода, такие как линтеры и сканеры уязвимостей, могут найти множество просчетов до того, как те начнут эксплуатироваться. RuboCop уведомит вас о проблемах безопасности, вроде небезопасной десериализации или IO hijacking, а также предложит варианты исправления.

Brakeman – сканер уязвимостей, который может найти SQL-инъекцию среди других ошибок безопасности. Оба инструмента являются бесплатными и поставляются с открытыми исходными текстами.

DeepSource – комплексное ПО, способное сканировать код, на каждом коммите, анализировать результаты работы линтеров/анализаторов и автоматически устранять множество проблем.

Для большинства языков программирования это решение имеет собственные пользовательские анализаторы, которые постоянно совершенствуются и обновляются. А еще оно элементарно устанавливается – нужно всего лишь добавить .deepsource.toml файл в корень репозитория и DeepSource готов:

         version = 1  [[analyzers]] name = "ruby" enabled = true  [[transformers]] name = "rubocop" enabled = true     

Заключение

Проблемы с безопасностью – самые опасные и дорогостоящие. Будьте предельно внимательны и учитывайте все вышесказанное в своих проектах. Обязательно продолжайте изучать язык программирования, ошибки других разработчиков и нашу статью про ошибки новичков. Удачи!

  • 0 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Свежие комментарии

    Рубрики

    About Author 01.

    Roman Spiridonov
    Roman Spiridonov

    Привет ! Мне 38 лет, я работаю в области информационных технологий более 4 лет. Тут собрано самое интересное.

    Our Instagram 04.

    Categories 05.

    © Speccy 2020 / All rights reserved

    Связаться со мной
    Close