Периодически возникает вопрос, как можно безопасно хранить логин и пароль в R, не задавая эти данные в явном виде в вашем скрипте. Мне кажется, есть несколько возможных решений. Можно хранить ваши параметры:
Давайте рассмотрим основную идею, преимущества (или недостатки) каждого из подходов.
[От переводчика: упорядочено по мере возрастания полезности.]
Первый подход — хранить ваши параметры прямо в скрипте.
Несмотря на простоту, никто всерьез не предлагает это делать из-за очевидного недостатка: нельзя показать свой код, не показывая также эти параметры.
Второй вариант реализуется почти так же просто. Идея такая: вы помещаете ваши параметры в отдельный файл внутри этой же папки с проектом, например, «keys.R». Потом можно читать параметры, используя, скажем,
Недостаток состоит в том, что можно все же случайно показать этот файл, если вы недостаточно внимательны.
Третий вариант — хранить параметры в одном из файлов .Rprofile. Эта опция довольно популярна, поскольку:
Можно хранить данные в другой папке, т.е. не в папке с проектом. Следовательно, менее вероятно, что вы случайно откроете доступ к файлу.
В .Rprofile можно писать обычный код на R.
Один из недостатков определения объектов
Немного более гибкая версия этого же способа — также использовать .Rprofile, но объявить ваши параметры переменными окружения. Для задания переменных окружения можно использовать
В R также есть механизм определения переменных окружения в специальном внешнем файле, .Renviron. Работа с .Renviron аналогична .Rprofile. Главная разница состоит в том, что в .Renviron можно задавать переменные напрямую, без использования
Переменные окружения не зависят от языка.
Формат json в основном используется для взаимодействия через веб сервисы. Поэтому большинство современных языков легко интерпретируют файлы json. То же относится к yaml файлам. Если вы хотите хранить параметры в формате, понятном другим языкам, например, Python, json может быть хорошим решением.
Один большой недостаток всех предыдущих подходов в том, что во всех случаях параметры хранятся в незашифрованном виде где-то на диске. Возможно, вы используете какой-то инструмент вроде keychain или LastPass.
Для хранения можно использовать зашифрованный диск. Как только он монтирован, с его содержимым можно работать как с обычным текстом. Т.е. с точки зрения пользователя R секретные данные просто хранятся как «обычный текст» где-то на зашифрованном диске.
Еще одна альтернатива — использовать пакет digest, который поддерживается Дирком Эддельбюттелем. Стефан Дуайен предложил такое решение:
Таким образом, секретные данные не появляются в явном виде или в коде. Это дает дополнительную возможность — хранить параметры где-то в другом месте (удаленный сервер, usb-диск и т.д.). Также это решение не требует ввода пароля каждый раз.
Стефан предлагает вот такой код для иллюстрации:
Другой вариант — использовать пакет sodium, созданный Йеруном Омсом. Пакет sodium — обертка на R для криптографической библиотеки libsodium.
Обертка для libsodium: современная, простая в использовании библиотека для шифрования, дешифрования, подписи, хеширования паролей и т.д. Sodium использует curve25519, новейшую функцию Диффи-Хеллмана от Даниэля Бернштейна, ставшую очень популярной после того, как в NSA обнаружилась уязвимость Dual EC DRBG.
Это значит, что sodium можно использовать для установки безопасного взаимодействия, в том числе с применением асимметричных ключей, непосредственно из R. Чтобы использовать sodium для шифрования ваших данных, воспользуйтесь подходом, описанным выше для digest.
Наконец, последняя опция — использовать пакет secure, написанный Хедли Викхемом. Из описания пакета:
Пакет secure предоставляет безопасное хранилище в публично доступном репозитории. Это позволяет хранить секретные данные в публичном репозитории, чтобы они были доступны только избранным пользователям. Это особенно полезно для тестирования, поскольку используя пакет, можно хранить личные данные в публичном репозитории, не показывая их всему миру.
Secure построен на основе асимметричного шифрования (с публичным и приватным ключами). Secure генерирует случайный главный ключ и использует его для шифрования (AES256) каждого файла в
Для того, чтобы понять, как это работает, может потребоваться целое исследование. Но в целом идея такая:
Хедли приводит пошаговую инструкцию по использованию пакета в своем github-репозитории.
- Непосредственно в скрипте.
- В файле внутри папки с проектом, который вы не показываете.
- В файле .Rprofile.
- В файле .Renviron.
- В json файле.
- В безопасном хранилище, к которому вы обращаетесь из R.
- Используя пакет digest.
- Используя пакет sodium.
- Используя пакет 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, который поддерживается Дирком Эддельбюттелем. Стефан Дуайен предложил такое решение:
- Я использую пакет digest c реализованным AES шифрованием.
- Использую две функции: одна пишет файлы, зашифрованные AES, вторая читает и расшифровывает эти файлы. Эти функции есть на github.
- Потом использую пакет digest, чтобы сгенерировать ключ для расшифровки и зашифровки файлов.
- Как только все это готово, я создаю блок данных (dataframe) с логином и паролем.
- Я использую функцию
write.aes()
, чтобы записать параметр локально в зашифрованный файл. 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-репозитории.