This work-in-progress chapter includes guidance about managing secrets in packages and links for further reading.
3 Package Development Security Best Practices
3.1 Miscellaneous
We recommend the article Ten quick tips for staying safe online by Danielle Smalls and Greg Wilson.
3.2 GitHub access security
We recommend you secure your GitHub account with two-factor (authentication) 2FA. It is compulsory for all ropensci GitHub organization members and outside collaborators so make sure to enable it before your package is approved.
We also recommend you regularly check who has access to your package repository, and that you prune any unused access (such as from former collaborators).
3.3 https
- If the web service your package wraps has either https or http, opt for https.
3.4 Secrets in packages
This section contains guidance for when you develop a package interacting with a web resource requiring credentials (API keys, tokens, etc.). Also refer to the httr
vignette about sharing secrets.
3.4.1 Secrets in packages and user protection
Say your package needs an API key for making requests on behalf of users of your package.
-
In your package documentation, guide the user so the API key doesn’t end up in the .Rhistory/script of users of your package.
Encourage the use of environment variables to store the API key (or even remove the possibility to pass it as an argument to the functions?). You could link to this intro to startup files and
usethis::edit_r_environ()
.Or your package could depend on, or encourage the use of,
keyring
to help user store variables in the specific OS’ credential stores (more secure than .Renviron): i.e. you’d create a function for setting the key, and have another one for retrieving the key; or the user would writeSys.setenv(SUPERSECRETKEY = keyring::key_get("myservice"))
at the beginning of their script.Do not print the API key even in verbose mode in any message, warning, error.
In the GitHub issue template, it should be stated not to share any credentials. If an user of your package accidentally shares credentials in an issue, make sure they’re aware of that so they can revoke the key (i.e. ask them explicitly in an answer whether they realized they shared their key).
3.4.2 Secrets in packages and development
You’ll need to protect your secrets as you protect secrets of users, but there’s more to take into account and keep in mind.
3.4.2.1 Secrets and recorded requests in tests
If you use vcr
or httptest
in tests for caching API responses, you need to make sure the recorded requests / fixtures do not contain secrets. Refer to vcr
security guidance and httptest
guidance “Redacting and Modifying Recorded Requests”, and inspect your recorded requests / fixtures before committing them the first time to be sure you got the setup right.
vcr
being an rOpenSci package, you can post any question you might have to rOpenSci forum.
3.4.2.3 Secrets and collaborations
What about pull requests from external contributors? On GitHub for instance, secrets are only available for GitHub Actions for pull requests started from the repository itself, not from fork. Tests using your secrets will fail unless you use some sort of mocked/cached response, so you might want to skip them depending on the context. For instance, in your CI account you could create an environment variable called THIS_IS_ME
and then skip tests based on the presence of this variable. This obviously means the PR checks by the CI are not exhaustive, so you’ll need to check out the PR locally to run all tests.
Document the behavior of your package for external PRs in CONTRIBUTING.md for the sake of people making PRs and of people reviewing them (you in a few weeks, and other authors of the package).
3.4.3 Secrets and CRAN
On CRAN, skip any tests (skip_on_cran()
) and examples (dontrun
) requiring credentials.
Precompute vignettes requiring credentials.
3.5 Further reading
Useful security resources:
rOpenSci community call “Security for R” (recording, slides, etc. see in particular the list of resources);