Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add constant time check for shared secret #2319

Merged
merged 4 commits into from Feb 11, 2019
Merged

Conversation

jcheng5
Copy link
Member

@jcheng5 jcheng5 commented Feb 4, 2019

Here's an exaggerated test to show that comparing two raw vectors using identical isn't constant-time, but using constantTimeEquals() is:

library(microbenchmark)

# Very long string
str1 <- paste0(rep(letters, 10000), collapse = "")
# Just the last character is different
str2 <- sub(".$", "1", str1, perl = TRUE)
# Just the first character is different
str3 <- sub("^.", "1", str1, perl = TRUE)

raw1 <- charToRaw(str1)
raw2 <- charToRaw(str2)
raw3 <- charToRaw(str3)

# These times are very different
microbenchmark(
  identical(raw1, raw2),
  identical(raw1, raw3)
)

# These times are very similar
microbenchmark(
  shiny:::constantTimeEquals(raw1, raw2),
  shiny:::constantTimeEquals(raw1, raw3)
)

Testing notes

Should verify that:

  1. Shiny Server (OS and Pro), Connect, ShinyApps.io all still work
  2. Use Shiny Server OS or Pro to cause an R process to launch on localhost, then figure out what port it's listening on and try to access https://localhost:port; this should fail.

@jcheng5 jcheng5 force-pushed the joe/misc/constant-time-check branch from 8c8473f to 665a665 Compare February 4, 2019 21:20
@jcheng5 jcheng5 requested a review from wch February 4, 2019 22:28
wch
wch previously approved these changes Feb 5, 2019
Copy link
Collaborator

@wch wch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Just one small comment.

R/server.R Outdated
@@ -161,7 +161,7 @@ createAppHandlers <- function(httpHandlers, serverFuncSource) {
# This value, if non-NULL, must be present on all HTTP and WebSocket
# requests as the Shiny-Shared-Secret header or else access will be
# denied (403 response for HTTP, and instant close for websocket).
sharedSecret <- getOption('shiny.sharedSecret')
sharedSecret <- loadSharedSecret()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A name like checkSharedSecret (as is used in the tests) would be a little clearer.

R/utils.R Outdated
constantTimeEquals <- function(raw1, raw2) {
stopifnot(is.raw(raw1))
stopifnot(is.raw(raw2))
stopifnot(length(raw1) == length(raw2))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it matter that this could reveal the length using a timing attack?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After discussion, we're not going to digest the input.

- Rename sharedSecret variables to checkSharedSecret
- Don't perform the digest::digest(). This just means the timing could
  give away the length of the secret, but that's OK, there's enough
  entropy in the secret even if you know its length.
@jcheng5
Copy link
Member Author

jcheng5 commented Feb 5, 2019

@wch Can you review this last commit? Thanks!

@wch wch merged commit 95173f6 into master Feb 11, 2019
@wch wch deleted the joe/misc/constant-time-check branch February 11, 2019 21:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants