(Acest articol a fost publicat pentru prima dată pe Rtaskși a contribuit cu drag la R-Bloggers). (Puteți raporta problema despre conținutul de pe această pagină aici)
Doriți să vă împărtășiți conținutul pe R-Bloggers? Faceți clic aici dacă aveți un blog sau aici dacă nu.
Puteți citi postarea originală în formatul său original pe site -ul RTASk de către Thinkr aici: Security Blind Spots in Shiny: De ce aplicația dvs. este mai vulnerabilă decât credeți
Buckle Up, vorbim despre securitate …
În timp ce dezvolta aplicația Signature.Py, Thinkr s -a confruntat recent cu o problemă de securitate – în Python – care merită atenția noastră deplină.
Versiunea pe GitHub, codul Signature.py este analizat de Dependabot. Acesta este un robot care scanează automat proiectele pentru dependențe învechite sau vulnerabile și poate sugera măsuri corective.
Dependabot ne -a alertat la o vulnerabilitate de securitate legată de utilizarea jinja2Și acest lucru ne -a condus la o reflecție mai largă: suntem, în calitate de R și dezvoltatori de aplicații strălucitoare, preocupați și de aceste probleme de securitate?
Răspunsul este un răsunător DA.
Despre ce vorbim exact?
Impactul problemei identificate în aplicația noastră Python a fost potențial grav: execuția codului neintenționat pe backend, accesând întreaga infrastructură a mașinii. Din fericire, Signature.Py manipulează doar șirurile de personaje și actualizarea jinja2 a fost capabil să remedieze alerta.
Dar ce zici de ecosistemul R? Dezvoltatorii strălucitori sunt expuși și acestor tipuri de vulnerabilități?
Injecție de cod: un pericol omniprezent
Injecția de cod este o vulnerabilitate comună de securitate care implică injectarea codului rău intenționat într -o pagină sau o aplicație. Acest cod este apoi executat, creând încălcarea securității. Există mai multe moduri de a injecta cod într -o aplicație, iar Shiny nu este, din păcate, imun la aceste riscuri.
Vulnerabilități în strălucire
Vestea bună
„Shiny este bine conceput„! În mod implicit, intrări în returnare strălucitoare (aproape) doar șiruri de caractere. Atâta timp cât conținutul de intrare nu este evaluat, suntem relativ siguri.
De exemplu, această aplicație simplă care afișează pur și simplu conținutul introdus de utilizator este securizat:
library(shiny)
ui <- fluidPage(
textInput("template", "Enter something"),
textOutput("result")
)
server <- function(input, output, session) {
output$result <- renderText({
paste("Your choice is", input$template)
})
}
shinyApp(ui, server)
Vestea proastă
Conținutul de intrare poate fi evaluat și introduce un defect de securitate. De îndată ce începem să evaluăm conținutul unei intrări, apar riscuri.
Cele trei mari familii de injecție în aplicații web
1. XSS (scripturi încrucișate): partea clientului
JavaScript sau codul CSS este injectat și executat în browserul altui utilizator. În strălucire, acest lucru se poate întâmpla atunci când redăm HTML direct de la intrarea utilizatorului fără a -l scăpa corect.
De exemplu, această aplicație este vulnerabilă:
library(shiny)
ui <- fluidPage(
textInput("text", "Enter something"),
uiOutput("result")
)
server <- function(input, output, session) {
output$result <- renderUI({
HTML(input$text)
})
}
shinyApp(ui, server)
În intrarea textului, un utilizator rău intenționat ar putea intra Sau chiar mai mult cod periculos, cum ar fi:
În următoarea aplicație vulnerabilă, puteți copia/lipi întregul fragment de cod în aplicația de mai jos, cu etichete pentru a vedea amenințarea.
Puteți scrie un prim comentariu și apoi copiați/lipiți codul pentru a vedea vulnerabilitatea.
Nu vă faceți griji aici, nu stocăm informațiile într -o bază de date 😉
library(shiny)
ui <- fluidPage(
h2("💬 User comments"),
textInput("pseudo", "Your username:", "Guest"),
textAreaInput("message", "Your message:", rows = 3),
actionButton("envoyer", "Send"),
tags$hr(),
h3("💬 Received comments:"),
uiOutput("commentaires")
)
server <- function(input, output, session) {
commentaires <- reactiveVal(
data.frame(
pseudo = character(),
message = character()
)
)
observeEvent(input$envoyer, {
new_entry <- data.frame(
pseudo = input$pseudo,
message = input$message
)
commentaires(rbind(commentaires(), new_entry))
})
output$commentaires <- renderUI({
coms <- commentaires()
if (nrow(coms) == 0) {
return(NULL)
}
HTML(paste0(
apply(coms, 1, function(row) {
glue::glue("{row(('pseudo'))} : {row(('message'))}
")
}),
collapse = "n"
))
})
}
shinyApp(ui, server)
Există două tipuri principale de XSS:
- A reflectat XSS: Codul rău intenționat este executat imediat.
- XSS depozitat: Codul rău intenționat este stocat într -o bază de date și executat atunci când alți utilizatori accesează pagina.
Amenințarea XSS depozitată este deosebit de în vedere. În exemplul de mai sus, acest cod JavaScript rău intenționat adaugă un buton de conectare falsă care va apărea pentru toți utilizatorii viitori ai aplicației. Imaginează -ți scenariul: un atacator injectează acest cod în aplicația dvs. și toți utilizatorii ulterior văd un buton roșu care solicită o autentificare. Ei fac clic pe el, introduc acreditările lor … și aceste informații pot fi preluate cu ușurință de către atacator.
2. Injecție de comandă: partea serverului
Codul este executat direct pe server. Acest lucru se poate întâmpla în strălucire atunci când evaluăm direct conținutul unei intrări.
Luați în considerare această aplicație aparent inofensivă:
library(shiny)
library(glue)
ui <- fluidPage(
textInput("template", "Enter something"),
textOutput("result")
)
server <- function(input, output, session) {
output$result <- renderText({
glue(input$template)
})
}
shinyApp(ui, server)
Această aplicație este de fapt vulnerabilă, deoarece glue() evaluează automat ceea ce este între {}. Un utilizator rău intenționat ar putea intra {system("ls /", intern = TRUE)} sau mai rău {system("rm -rf /")}.
system()este o funcție R care execută comenzi shell.
În timp cesystem("ls /", intern = TRUE)prezintă un risc relativ, deoarece afișează doar conținutul directorului rădăcină al computerului,{system("rm -rf /")}este o comandă potențial distructivă. Această comandă va încerca să șteargă toate fișierele din sistem! Mâner cu precauție 😉
Utilizarea glue() Pare inofensivă, totuși glue::glue(input$template) se ridică la același lucru ca eval(parse(text = input$template)). eval(parse(text = ...)) Duo este o operație care va încerca să evalueze textul. Utilizarea dezvoltatorului a acestor funcții este clară: încearcă să evalueze textul. Cu toate acestea, utilizarea glue() Funcția este mai insidioasă aici.
3. Injecție SQL: partea bazei de date
Codul SQL este executat în baza de date. Acest lucru se poate întâmpla atunci când construim întrebări SQL de la intrările utilizatorilor fără a le scăpa corect.
De exemplu, această aplicație este vulnerabilă:
library(shiny)
library(DBI)
library(RSQLite)
con <- dbConnect(RSQLite::SQLite(), ":memory:")
dbExecute(
conn = con,
"CREATE TABLE users (
id INTEGER, name TEXT
)"
)
dbExecute(
conn = con,
"INSERT INTO users (id, name)
VALUES
(1, 'Arthur'),
(2, 'Adrien'),
(3, 'Lucas'),
(4, 'Lily'),
(5, 'Margot')"
)
ui <- fluidPage(
h2("SQL Injection Test"),
textInput(
inputId = "user_input",
label = "Select only 1 ID to find your individual",
value = 1
),
actionButton(
inputId = "submit",
label = "Submit"
),
tableOutput(
outputId = "result"
)
)
server <- function(input, output, session) {
rv <- reactiveValues()
observeEvent(input$submit, {
req(input$user_input)
rv$query <- paste0(
"SELECT * FROM users WHERE id = ",
input$user_input
)
rv$result <- dbGetQuery(con, rv$query)
})
output$result <- renderTable({
req(rv$result)
rv$result
})
}
shinyApp(ui, server)
Un utilizator ar putea intra 1 OR 1=1 Pentru a prelua toate datele din tabel sau chiar 1; DROP TABLE users; Pentru a șterge masa.
Cum să te protejezi?
Regula de Aur: Nu aveți încredere niciodată la intrarea utilizatorului, indiferent de context
Iată câteva bune practici pentru a vă asigura aplicațiile strălucitoare:
- Pentru XSS: Utilizare
htmltools::htmlEscape()Pentru a scăpa de etichete HTML sau JavaScript dacă trebuie să le stocați într -o bază de date.
htmltools::htmlEscape("")
(1) ""
- Pentru injecții de comandă:
- Evitați să folosiți
eval(parse(text = input$template))sau controlați strict intrarea utilizatorului. - Manipulează doar șirurile de caractere fără a le evalua.
- Filtrați și securizați alegerile utilizatorilor cu
match.arg(),switch()sau condiționatifstructuri. - Folosiți a
sandboxCând este necesară evaluarea. - Pentru fișierele încărcate, preferați formate simple precum
.csvsau.txt.
- Evitați să folosiți
- Pentru injecții SQL:
- Utilizare
sqlInterpolate()mai degrabă decât să construiască întrebări cupaste():
- Utilizare
# Vulnerable
query_vuln <- paste0("
SELECT *
FROM users
WHERE id = ", input$user_input
)
# Secure
query_str <- "
SELECT *
FROM users
WHERE id = ?id
"
query <- sqlInterpolate(con, query_str, id = input$user_input)
query
#
# SELECT *
# FROM users
# WHERE id = 1
dbGetQuery(con, query)
Concluzie
Securitatea este un aspect crucial al dezvoltării aplicațiilor web, inclusiv aplicații strălucitoare. Nu subestimați niciodată riscurile legate de intrările utilizatorilor nevalidate sau neexecutate. Urmând câteva cele mai bune practici simple, puteți îmbunătăți semnificativ securitatea aplicațiilor dvs.
Nu ezitați să ne contactați dacă doriți să discutați despre securitatea aplicațiilor dvs. strălucitoare!
Puteți găsi, de asemenea, toate sesiunile noastre de pregătire viitoare pentru a descoperi dezvoltarea de aplicații strălucitoare aici!
Această postare este mai bine prezentată pe site -ul său original Thinkr aici: Security Blind Spots in Shiny: De ce aplicația dvs. este mai vulnerabilă decât credeți
