错误:由于会话密钥已更改而无法解密密钥|Shinyapps.io |[R [英] Error : Failed to decrypt key as session key has changed | shinyapps.io | R

查看:66
本文介绍了错误:由于会话密钥已更改而无法解密密钥|Shinyapps.io |[R的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:这个(冗长)问题是我

1.用于加密数据帧的代码.

 库(钠)#>警告:软件包"sodium"是在R版本3.5.3下构建的库(加密器)#>警告:软件包"encryptr"是在R版本3.5.3下构建的图书馆(cyphr)#>警告:软件包"cyphr"是在R版本3.5.3下构建的#>#>附件包:"cyphr"#>以下对象被"package:encryptr"屏蔽:#>#>解密,解密文件,加密,加密文件#setting本地工作目录#setwd("D://Work/03Mar20/")df = data.frame(用户= c(用户1",用户2",用户3",用户4",用户5"),密码= c("pass1","pass2","pass3","pass4","pass5"),权限= c("admin","admin","admin","admin","admin"),名称= c(用户一",用户二",用户三",用户四",用户五"),stringsAsFactors = FALSE)#使用cyphr和钠包生成密钥并加密所需的数据帧键<-cyphr :: key_sodium(sodium :: keygen())cyphr :: encrypt(saveRDS(df,"auth_base.rds"),密钥)#将密钥另存为.rds文件,并从R环境中删除saveRDS(key,"key.rds")rm(密钥)由reprex软件包(v0.3.0)创建于2020-03-06 

2.闪亮的应用程序代码(解密数据框并授权用户).

 库(发光)图书馆(shinydashboard)图书馆(shinydashboardPlus)图书馆(shinyauthr)图书馆(shinyjs)图书馆(钠)库(加密器)图书馆(cyphr)图书馆(胶水)图书馆(针织)库(rsconnect)库(ggplot2)图书馆(DT)#setting本地工作目录#setwd("D://与工作相关/03Mar20")键<-readRDS("key.rds")df = cyphr :: decrypt(readRDS("auth_base.rds"),密钥)#Dataframe,其中包含用户名,密码和其他用户数据凭证= data.frame(用户名= df $ user,密码= sapply(df $ password,sodium :: password_store),权限= df $权限,名称= df $名称,stringsAsFactors = FALSE)#主登录屏幕loginpage<-div(id ="loginpage",style ="width:500px; max-width:100%; margin:0 auto; padding:20px;",wellPanel(tags $ h2("LOG IN",class ="text-center",style ="padding-top:0; color:#333; font-weight:600;"),textInput("userName",placeholder ="Username",label = tagList(icon("user"),"Username")),passwordInput("passwd",placeholder ="Password",label = tagList(icon("unlock-alt"),"Password")),br(),div(style ="text-align:center;",actionButton("login","SIGN IN",style =颜色:白色; background-color:#3c8dbc;内边距:10px 15px;宽度:150像素;光标:指针;font-size:18px;字体粗细:600;),Shinyjs ::隐藏(div(id ="nomatch",tags $ p(用户名或密码错误!",style ="color:red; font-weight:600;padding-top:5像素; font-size:16像素;",class ="text-center"))),br())))标头< -dashboardHeader(title ="Template",uiOutput("logoutbtn"))侧边栏< -dashboardSidebar(collapsed = FALSE,uiOutput("sidebarpanel"))正文< -dashboardBody(shinyjs :: useShinyjs(),uiOutput("body"))ui< -dashboardPage(标题,侧边栏,正文,皮肤=蓝色")服务器<-功能(输入,输出,会话){登录名= FALSE用户<-reactValues(登录名=登录名)观察({如果(USER $ login == FALSE){如果(!is.null(input $ login)){如果(input $ login> 0){用户名<-隔离(input $ userName)密码<-隔离(input $ passwd)if(length(which(credentials $ username == Username))== 1){pasmatch<-凭证[密码"] [其中(凭证$ username == Username),]pasverify<-password_verify(密码匹配,密码)if(pasverify){USER $登录名<-TRUE} 别的 {Shinyjs :: toggle(id ="nomatch",anim = TRUE,时间= 1,animType ="fade")Shinyjs :: delay(3000,Shinyjs :: toggle(id ="nomatch",anim = TRUE,time = 1,animType ="fade"))}} 别的 {Shinyjs :: toggle(id ="nomatch",anim = TRUE,时间= 1,animType ="fade")Shinyjs :: delay(3000,Shinyjs :: toggle(id ="nomatch",anim = TRUE,time = 1,animType ="fade"))}}}}})输出$ logoutbtn<-renderUI({req(USER $登录)tags $ li(a(icon("fa fa-sign-out"),注销",href ="javascript:window.location.reload(true)"),class ="dropdown",style ="background-color:#eee!important; border:0;font-weight:粗体;边距:5px;内边距:10px;)})output $ sidebarpanel<-renderUI({如果(USER $ login == TRUE){如果(凭证[,权限"] [其中(凭证$ username == input $ userName)] =="admin"){sidebarMenu(div(textOutput("permission"),style ="padding:20px"),menuItem("Data",tabName ="dashboard",icon = icon("table")))}}})output $ body<-renderUI({如果(USER $ login == TRUE){如果(凭证[,权限"] [其中(凭证$ username == input $ userName)] =="admin"){tabItems(tabItem(tabName ="dashboard",class ="active",fluidRow(框(宽度= 12,dataTableOutput('results')))))}}别的 {登入页面}})输出$权限<-renderText({如果(USER $ login == TRUE){paste("Permission:",凭据[,权限"] [其中(凭证$ username == input $ userName)])}})output $ results<-DT :: renderDataTable({datatable(mtcars,options = list(autoWidth = TRUE,搜索=假))})}ShinyApp(用户界面,服务器) 

我从错误中得知,当我将其发布到云中进行解密时, key 的会话ID在加密时是不匹配的.作为安全领域的新秀,有什么解决方法可以在云上实现解密?

任何建议都将不胜感激.

解决方案

问题原因

问题是设计的,因为 cyphr :: key_sodium 创建了一个仅对当前会话有效的密钥.也就是说,不可能在不同的会话之间共享它,更不用说在不同的系统上共享它了.

因此,问题根本与 shiny 本身无关,而是与您试图在不同会话中使用 cyphr 键的事实有关.

从小插图开始:

当使用key_openssl,keypair_openssl,key_sodium或keypair_sodium时,我们生成可以解密数据的东西.这些函数返回的对象可以加密和解密数据,因此有理由担心,如果将这些对象本身保存到磁盘上,则数据将受到损害.

为避免这种情况,cyphr不会直接在这些对象中存储私钥或对称密钥,而是使用特定于cyphr的会话密钥对敏感密钥进行加密,该会话密钥在每次加载程序包时都会重新生成.这意味着这些对象实际上仅在一个会话中有用,并且如果用save.image保存(可能在会话结束时自动保存),则密钥不能用于解密数据.

问题的解决方法

 库(cyphr)文件<-"encr.rds";df<-data.frame(a = 1)## cyphr工作流程将无法跨会话/系统工作键<-key_sodium(sodium :: keygen())加密(saveRDS(df,文件),密钥)##在同一会话中工作解密(readRDS(文件),密钥)##模拟会话更改(重新启动或其他系统)session_key_refresh()##不起作用解密(readRDS(文件),密钥)取消链接(文件) 

解决方案


注意.更新了代码,因为不需要保存和存储 nonce .


因此,您需要使用其他库来完成工作.例如,您可以使用库 sodium 本身:

 库(钠)key_file<-" key.rds"文件<-"encr.rds";键<-keygen()df<-data.frame(a = 1)msg<-serialize(df,NULL)密码<-data_encrypt(msg,密钥)saveRDS(密码,文件)##商店密钥saveRDS(key,key_file) 

您现在可以共享 key.rds (或将其放入闪亮的服务器中).要模拟该过程,只需重新启动R并运行:

 库(钠)key_file<-" key.rds"文件<-"encr.rds";键<-readRDS(key_file)#使用相同的存储密钥和随机数解密解密<-readRDS(文件)反序列化(data_decrypt(decipher,key))#   一种#1 1取消链接(key_file)取消链接(文件) 

安全问题

使用对称加密(就像您的示例一样,这是用于解密/加密的一个密钥)并将密钥存储在服务器的顶部听起来不是一个好主意.任何人只要得到您的密钥文件,就可以解密您的秘密.

我自己不是安全专家,但我会重新考虑您的设计.

Note: This (lengthy) question is a follow-up to my previous post.

I would like to achieve the encryption of data locally (local RStudio) and decrypt the encrypted data remotely (application hosted on shinyapps.io).

The first part of the code intrinsically encrypts a data-frame using a key. The second part of the code is a shiny application that decrypts the data-frame using the same key and thereby using this data-frame for authentication purposes within the application. The code works just alright on my machine.

However, it throws an error when published to shinyapps.io (cloud-based hosting service) as shown below:

1.Code for encrypting the data-frame.

library(sodium)
#> Warning: package 'sodium' was built under R version 3.5.3
library(encryptr)
#> Warning: package 'encryptr' was built under R version 3.5.3
library(cyphr)
#> Warning: package 'cyphr' was built under R version 3.5.3
#> 
#> Attaching package: 'cyphr'
#> The following objects are masked from 'package:encryptr':
#> 
#>     decrypt, decrypt_file, encrypt, encrypt_file

#setting local working directory 
#setwd("D://Work/03Mar20/")

df = data.frame(
  user = c("user1", "user2", "user3", "user4", "user5"),
  password = c("pass1", "pass2", "pass3", "pass4", "pass5"),
  permissions = c("admin","admin","admin","admin","admin"),
  name = c("user one", "user two", "user three", "user four", "user five"),
  stringsAsFactors = FALSE
)

#generating a key and encrypting the desired dataframe using cyphr and sodium packages
key <- cyphr::key_sodium(sodium::keygen())
cyphr::encrypt(saveRDS(df, "auth_base.rds"), key)

#saving the key as a .rds file and removing from R environment
saveRDS(key, "key.rds")
rm(key)


Created on 2020-03-06 by the reprex package (v0.3.0)

2.Code for shiny application (decrypting the data-frame and authorizing users).

library(shiny)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyauthr)
library(shinyjs)
library(sodium)
library(encryptr)
library(cyphr)
library(glue)
library(knitr)
library(rsconnect)
library(ggplot2)
library(DT)

#setting local working directory 
#setwd("D://Work Related/03Mar20")

key <- readRDS("key.rds")
df = cyphr::decrypt(readRDS("auth_base.rds"), key)

#Dataframe that holds usernames, passwords and other user data
credentials = data.frame(
    username = df$user,
    password = sapply(df$password, sodium::password_store),
    permission = df$permissions, 
    name = df$name,
    stringsAsFactors = FALSE
)

# Main login screen
loginpage <- div(id = "loginpage", style = "width: 500px; max-width: 100%; margin: 0 auto; padding: 20px;",
                 wellPanel(
                     tags$h2("LOG IN", class = "text-center", style = "padding-top: 0;color:#333; font-weight:600;"),
                     textInput("userName", placeholder="Username", label = tagList(icon("user"), "Username")),
                     passwordInput("passwd", placeholder="Password", label = tagList(icon("unlock-alt"), "Password")),
                     br(),
                     div(
                         style = "text-align: center;",
                         actionButton("login", "SIGN IN", style = "color: white; background-color:#3c8dbc;
                                 padding: 10px 15px; width: 150px; cursor: pointer;
                                 font-size: 18px; font-weight: 600;"),
                         shinyjs::hidden(
                             div(id = "nomatch",
                                 tags$p("Incorrect username or password!",
                                        style = "color: red; font-weight: 600; 
                                            padding-top: 5px;font-size:16px;", 
                                        class = "text-center"))),
                         br()
                     ))
)

header <- dashboardHeader( title = "Template", uiOutput("logoutbtn"))
sidebar <- dashboardSidebar(collapsed = FALSE, uiOutput("sidebarpanel")) 
body <- dashboardBody(shinyjs::useShinyjs(), uiOutput("body"))

ui<-dashboardPage(header, sidebar, body, skin = "blue")

server <- function(input, output, session) {

    login = FALSE
    USER <- reactiveValues(login = login)

    observe({ 
        if (USER$login == FALSE) {
            if (!is.null(input$login)) {
                if (input$login > 0) {
                    Username <- isolate(input$userName)
                    Password <- isolate(input$passwd)
                    if(length(which(credentials$username==Username))==1) { 
                        pasmatch  <- credentials["password"][which(credentials$username==Username),]
                        pasverify <- password_verify(pasmatch, Password)
                        if(pasverify) {
                            USER$login <- TRUE
                        } else {
                            shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade")
                            shinyjs::delay(3000, shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade"))
                        }
                    } else {
                        shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade")
                        shinyjs::delay(3000, shinyjs::toggle(id = "nomatch", anim = TRUE, time = 1, animType = "fade"))
                    }
                } 
            }
        }    
    })

    output$logoutbtn <- renderUI({
        req(USER$login)
        tags$li(a(icon("fa fa-sign-out"), "Logout", 
                  href="javascript:window.location.reload(true)"),
                class = "dropdown", 
                style = "background-color: #eee !important; border: 0;
                    font-weight: bold; margin:5px; padding: 10px;")
    })

    output$sidebarpanel <- renderUI({
        if (USER$login == TRUE ){ 
            if (credentials[,"permission"][which(credentials$username==input$userName)]=="admin") {
                sidebarMenu(
                    div(textOutput("permission"), style = "padding: 20px"),
                    menuItem("Data", tabName = "dashboard", icon = icon("table"))
                    )
            }
        }
    })



    output$body <- renderUI({
        if (USER$login == TRUE ) {
            if (credentials[,"permission"][which(credentials$username==input$userName)]=="admin") {
                tabItems(
                    tabItem(
                        tabName ="dashboard", class = "active",
                        fluidRow(
                            box(width = 12, dataTableOutput('results'))
                        ))
                )
            }

        }
        else {
            loginpage
        }
    })

    output$permission <- renderText({
        if (USER$login == TRUE ) {
            paste("Permission: ", credentials[,"permission"][which(credentials$username==input$userName)])
        }    
    })

    output$results <-  DT::renderDataTable({
        datatable(mtcars, options = list(autoWidth = TRUE,
                                         searching = FALSE))
    })

}

shinyApp(ui, server)

I learn from the error that the key's session ID at the time of encryption isn't matching when I publish it to the cloud for decryption. As a rookie in the security domain, is there's any workaround to achieve the decryption on the cloud?

Any suggestions are much appreciated.

解决方案

Reason for the issue

The problem is by design as cyphr::key_sodium creates a key which is valid, well, only for the current session. That is, it is not possible to share it across different session let alone different systems.

Hence, the problem is not at all related to shinyitself, but to the fact that you are trying to use cyphr keys across different sessions.

From the vignette:

When using key_openssl, keypair_openssl, key_sodium, or keypair_sodium we generate something that can decrypt data. The objects that are returned by these functions can encrypt and decrypt data and so it is reasonable to be concerned that if these objects were themselves saved to disk your data would be compromised.

To avoid this, cyphr does not store private or symmetric keys directly in these objects but instead encrypts the sensitive keys with a cyphr-specific session key that is regenerated each time the package is loaded. This means that the objects are practically only useful within one session, and if saved with save.image (perhaps automatically at the end of a session) the keys cannot be used to decrypt data.

Reprex of the issue

library(cyphr)

file <- "encr.rds"
df <- data.frame(a = 1) 

## cyphr workflow won't work across sessions / systems

key <- key_sodium(sodium::keygen())

encrypt(saveRDS(df, file), key)

## works within the same session
decrypt(readRDS(file), key)

## simulate session change (restart or other system)
session_key_refresh()

## won't work
decrypt(readRDS(file), key)
unlink(file)

Solution


Note. Updated the code as it is not needed to save and store the nonce.


Thus, you need to use a different library for doing the work. You can use for example the library sodium itself:

library(sodium)
key_file <- "key.rds"
file <- "encr.rds"
key <- keygen()

df <- data.frame(a = 1)
msg <- serialize(df, NULL)

cipher <- data_encrypt(msg, key)
saveRDS(cipher, file)

## store key
saveRDS(key, key_file)

You can share now key.rds (or put it to your shiny server). To simulate that simply restart your R and run:

library(sodium)
key_file <- "key.rds"
file <- "encr.rds"
key <- readRDS(key_file)

# Decrypt with same stored key and nonce
decipher <- readRDS(file)
unserialize(data_decrypt(decipher, key))

#   a
# 1 1

unlink(key_file)
unlink(file)

Security Concerns

Using a symmetric encryption (that is one key for de-/encrypting, like in your example) and storing the key on top on the server does not sound like a good idea. Anybody getting his hands on your key file will be able to decrypt your secrets.

I am not a security expert myself, but I would re-consider your design.

这篇关于错误:由于会话密钥已更改而无法解密密钥|Shinyapps.io |[R的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆