22.3 Compute resources
What about errors, holes, dangerous function combinations, selective characters such as special characters that are not recognizable by the system language? What can be done to prevent unexpected values from being used?
Malicious attackers can:
- run any R code they want
- delete important files
- modify data
- send confidential data back to the user of the app
You should never:
source()
an uploaded .R filermarkdown::render()
an uploaded .Rmd
Mind:
- the combination of
parse()
andeval()
as big warning sign for any Shiny app
Danger:
- it is possible to ask R to execute arbitrary code inside a model formula
Below are some examples of dangerous app chunks:
1 - This first example shows how the combination of parse()
and eval()
allows the user to insert only numeric values.
server <- function(input, output, session) {
output$results <- renderText({
eval(parse(text = input$code))
})
}
2 - Other kinds of possibilities for error chain generators are given by the model formula, the formula can executes an extra command print()
lm(y ~ {print("Hi!"); x}, data = df)
#> [1] "Hi!"
3 - Third warning comes from the {glue} package, the book in this case suggests to pay attention to use:
glue::glue("{title}-{print('Hi'); number}")
and suggests to substitute with glue_safe()
which instead releases an error/warning:
glue::glue_safe("{title}-{print('Hi'); number}")
#> Error in .transformer(expr, env): object 'print('Hi'); number' not found
4 - In case of variable transformation, this is what can happen:
The code to construct a SQL query with paste()
could lead to flexibilities in code construction that would allow the addition of extra characters not warned or advised by any friendly error messages
find_student <- function(name) {
paste0("SELECT * FROM Students WHERE name = ('", name, "');")
}
find_student("Hadley")
## [1] "SELECT * FROM Students WHERE name = ('Hadley');"
## [1] "SELECT * FROM Students WHERE name = ('Robert'); DROP TABLE Students; --');"
solution: use glue::glue_sql()
con <- DBI::dbConnect(RSQLite::SQLite(), ":memory:")
find_student <- function(name) {
glue::glue_sql("SELECT * FROM Students WHERE name = ({name});", .con = con)
}
find_student("Robert'); DROP TABLE Students; --")
## <SQL> SELECT * FROM Students WHERE name = ('Robert''); DROP TABLE Students; --');