Как безопасно хранить и использовать в R секретные данные

в 14:15, , рубрики: R, Блог компании Инфопульс Украина

Периодически возникает вопрос, как можно безопасно хранить логин и пароль в R, не задавая эти данные в явном виде в вашем скрипте. Мне кажется, есть несколько возможных решений. Можно хранить ваши параметры:

  1. Непосредственно в скрипте.
  2. В файле внутри папки с проектом, который вы не показываете.
  3. В файле .Rprofile.
  4. В файле .Renviron.
  5. В json файле.
  6. В безопасном хранилище, к которому вы обращаетесь из R.
  7. Используя пакет digest.
  8. Используя пакет sodium.
  9. Используя пакет secure.

Давайте рассмотрим основную идею, преимущества (или недостатки) каждого из подходов.
[От переводчика: упорядочено по мере возрастания полезности.]

Непосредственно в скрипте

Первый подход — хранить ваши параметры прямо в скрипте.

id <- "my login name"
pw <- "my password"
 
call_service(id, pw, ...)

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

В файле внутри папки с проектом, который вы не показываете

Второй вариант реализуется почти так же просто. Идея такая: вы помещаете ваши параметры в отдельный файл внутри этой же папки с проектом, например, «keys.R». Потом можно читать параметры, используя, скажем, source(). Потом мы исключаем «keys.R» из системы контроля версий. Если используете git, можно добавить «keys.R» в настройки .gitignore.

Недостаток состоит в том, что можно все же случайно показать этот файл, если вы недостаточно внимательны.

# keys.R
id <- "my login name"
pw <- "my password"
 
# script.R
source("keys.R")
call_service(id, pw, ...)

В файле .Rprofile

Третий вариант — хранить параметры в одном из файлов .Rprofile. Эта опция довольно популярна, поскольку:
Можно хранить данные в другой папке, т.е. не в папке с проектом. Следовательно, менее вероятно, что вы случайно откроете доступ к файлу.

В .Rprofile можно писать обычный код на R.

# ~/.Rprofile
id <- "my login name"
pw <- "my password"
 
# script.R
 
# id и pw определены в скрипте через .Rprofile
call_service(id, pw, ...)

Один из недостатков определения объектов "id" и "pw" в .Rprofile — они становятся частью глобального окружения. Раз они там, их легко изменить из скрипта. Например, использование rm() для того, чтобы очистить глобальное окружение, удалит их.

Немного более гибкая версия этого же способа — также использовать .Rprofile, но объявить ваши параметры переменными окружения. Для задания переменных окружения можно использовать Sys.setenv(), для чтения — Sys.getenv().

# ~/.Rprofile
Sys.setenv(id = "my login name")
Sys.setenv(pw = "my password")
 
# script.R
 
# id и pw определены в скрипте через .Rprofile
call_service(id = Sys.getenv("id"), pw = Sys.getenv("pw"), ...)

В файле .Renviron

В R также есть механизм определения переменных окружения в специальном внешнем файле, .Renviron. Работа с .Renviron аналогична .Rprofile. Главная разница состоит в том, что в .Renviron можно задавать переменные напрямую, без использования Sys.setenv().

Переменные окружения не зависят от языка.

# ~/.Renviron
id = "my login name"
pw = "my password"
 
# script.R
 
# id и pw определены в скрипте через .Renviron
call_service(id = Sys.getenv("id"), pw = Sys.getenv("pw"), ...)

В json или yaml файле

Формат json в основном используется для взаимодействия через веб сервисы. Поэтому большинство современных языков легко интерпретируют файлы json. То же относится к yaml файлам. Если вы хотите хранить параметры в формате, понятном другим языкам, например, Python, json может быть хорошим решением.

# keys.json
{
  "id":["my login name"],
  "pw":["my password"]
} 
 
# script.R
library(jsonlite)
call_service(id = fromJSON("keys.json")$id, 
             pw = fromJSON("keys.json")$pw, ...)

В безопасном хранилище, к которому вы обращаетесь из R

Один большой недостаток всех предыдущих подходов в том, что во всех случаях параметры хранятся в незашифрованном виде где-то на диске. Возможно, вы используете какой-то инструмент вроде keychain или LastPass.

Для хранения можно использовать зашифрованный диск. Как только он монтирован, с его содержимым можно работать как с обычным текстом. Т.е. с точки зрения пользователя R секретные данные просто хранятся как «обычный текст» где-то на зашифрованном диске.

Используя пакет digest

Еще одна альтернатива — использовать пакет digest, который поддерживается Дирком Эддельбюттелем. Стефан Дуайен предложил такое решение:

  1. Я использую пакет digest c реализованным AES шифрованием.
  2. Использую две функции: одна пишет файлы, зашифрованные AES, вторая читает и расшифровывает эти файлы. Эти функции есть на github.
  3. Потом использую пакет digest, чтобы сгенерировать ключ для расшифровки и зашифровки файлов.
  4. Как только все это готово, я создаю блок данных (dataframe) с логином и паролем.
  5. Я использую функцию write.aes(), чтобы записать параметр локально в зашифрованный файл.
  6. read.aes() позволяет расшифровать параметры и импортировать их в R.

Таким образом, секретные данные не появляются в явном виде или в коде. Это дает дополнительную возможность — хранить параметры где-то в другом месте (удаленный сервер, usb-диск и т.д.). Также это решение не требует ввода пароля каждый раз.

Стефан предлагает вот такой код для иллюстрации:

source("crypt.R")
load("key.RData")
 
credentials <- data.frame(login = "foo", password = "bar", stringsAsFactors = FALSE)
write.aes(df = credentials, filename = "credentials.txt",key = key)
rm(credentials)

credentials <- read.aes(filename = "credentials.txt",key = key)
print(credentials)

Используя пакет sodium

Другой вариант — использовать пакет sodium, созданный Йеруном Омсом. Пакет sodium — обертка на R для криптографической библиотеки libsodium.

Обертка для libsodium: современная, простая в использовании библиотека для шифрования, дешифрования, подписи, хеширования паролей и т.д. Sodium использует curve25519, новейшую функцию Диффи-Хеллмана от Даниэля Бернштейна, ставшую очень популярной после того, как в NSA обнаружилась уязвимость Dual EC DRBG.

Это значит, что sodium можно использовать для установки безопасного взаимодействия, в том числе с применением асимметричных ключей, непосредственно из R. Чтобы использовать sodium для шифрования ваших данных, воспользуйтесь подходом, описанным выше для digest.

Используя пакет secure

Наконец, последняя опция — использовать пакет secure, написанный Хедли Викхемом. Из описания пакета:

Пакет secure предоставляет безопасное хранилище в публично доступном репозитории. Это позволяет хранить секретные данные в публичном репозитории, чтобы они были доступны только избранным пользователям. Это особенно полезно для тестирования, поскольку используя пакет, можно хранить личные данные в публичном репозитории, не показывая их всему миру.

Secure построен на основе асимметричного шифрования (с публичным и приватным ключами). Secure генерирует случайный главный ключ и использует его для шифрования (AES256) каждого файла в vault/. Главный ключ нигде не хранится в незашифрованном виде, вместо этого для каждого пользователя хранится копия, зашифрованная его публичным ключом. Каждый пользователь может расшифровать главный ключ, используя свой приватный ключ, и потом применять его для расшифровки каждого файла.

Для того, чтобы понять, как это работает, может потребоваться целое исследование. Но в целом идея такая:

  • Секретные данные хранятся в репозитории с использованием ключа, состоящего из публичных ключей всех людей, которым вы хотите дать право на расшифровку.
  • Вы можете использовать публичный ключ, доступный каждому пользователю на github.
  • Также можно использовать публичный ключ Travis, если применяется непрерывная интеграция.

Хедли приводит пошаговую инструкцию по использованию пакета в своем github-репозитории.

Автор: Инфопульс Украина

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js