Shiny: Make client IP address available on clientData

Created on 5 Apr 2013  ·  22Comments  ·  Source: rstudio/shiny

Need to look out for x-forwarded-for headers, talk to me for more info.

Consult Team Advanced Medium Medium Type

Most helpful comment

Having access to the client's IP address would be really useful in my projects as well. Any chance we'll see this feature in an upcoming release?

All 22 comments

Also HTTP request headers.

Has any movement happened on this? @jcheng5

@coatless No, but what did you need it for?

I have a shiny application that is setup to allow students to submit homework. The app right now only is able to record usernames and submission times on action submit. The request for IP addresses being passed to the shiny app is because it would enable me to store the user's IP along with the submitted assignment. This would provide additional evidence to support an academic integrity violation due to two submissions being ranked similarly under my current setup.

Another vote for exposing request IP

Yet another vote.

If your app is deployed using Connect or shinyapps.io, there's a bit of a hacky way to do this. We're working on making this less hacky by making it accessible from the server-side (and at all in shiny server), so this is not the final word on the topic. I just wanted to share what does work today if case it's useful for any of you:

library(shiny)

ui_xfwd <- NULL

ui <- function(req) {
  if ("HTTP_X_FORWARDED_FOR" %in% ls(req)) ui_xfwd <<- req[["HTTP_X_FORWARDED_FOR"]]
  fluidPage(
    h3("result"),
    uiOutput("result")
  )
}

server <- function(input, output, session) {
  output$result <- renderUI({
    if (!is.null(ui_xfwd)) {
      div(
        p("HTTP_X_FORWARDED_FOR header present in UI:"),
        p(ui_xfwd)
      )
    } else {
      div(
        p("HTTP_X_FORWARDED_FOR header not present; here's the REMOTE_ADDR:"),
        p(session$request[["REMOTE_ADDR"]])
      )
    }
  })
}

shinyApp(ui, server)

I wouldn't do it this way--I'll post a more robust workaround in a bit.

@jcheng5 any eta on the robust method?

Sorry for the delay. The problem with the method by @bborgesr is that it assumes that whenever the server function runs, the immediately preceding UI request was from the same browser. That's not necessarily true--it's entirely possible that those requests are not consecutive due to overlapping traffic or maybe the UI being cached when the user hits the back button.

If you're not too worried about fraud then a more reliable way to do this is to inject the IP address into the generated UI. I wish we had an input binding for <input type="hidden"> but we don't current have that, so you can hide a text input instead.

library(shiny)

ui <- function(req) {
  fluidPage(
    div(style = "display: none;",
      textInput("remote_addr", "remote_addr",
        if (!is.null(req[["HTTP_X_FORWARDED_FOR"]]))
          req[["HTTP_X_FORWARDED_FOR"]]
        else
          req[["REMOTE_ADDR"]]
      )
    )
  )
}

server <- function(input, output, session) {
  cat("The remote IP is", isolate(input$remote_addr), "\n")
}

shinyApp(ui, server)

If you're more concerned about fraud then you'll unfortunately have to wait for the proper fix to land in our server products.

@jcheng5 is there an ETA on when the proper fix will land? (1 month, 6 months, year?)

@coatless Actually I took a closer look and this seems like it should already work with Shiny Server Pro (adding the following to /etc/shiny-server/shiny-server.conf):

whitelist_headers "x-forwarded-for";

Then you could look for session$req$HTTP_X_FORWARDED_FOR from within the server function.

This would be a good candidate for the next version of Shiny Server Open Source (cc @alandipert @shalutiwari) but there certainly will not be another release before rstudio::conf in early February. So at least a couple of months. Sorry I can't get more specific than that.

@jcheng5 excellent. A thousand thanks mate!

Never mind, the whitelist_headers don't work--the codepath it operates on doesn't have the right headers in the first place. Let me see if I can figure this out.

Notes to self (and @alandipert):

  • The xfwd implementation for node-http-proxy is not ideal; see here. It doesn't preserve existing X-Forwarded-For headers (if you're a proxy, you're supposed to append the remote-addr to the end of any existing X-Forwarded-For header if the upstream proxy is trusted). Ideally we wouldn't use this but instead would implement these headers ourselves in lib/proxy/http.js. Probably would want the administrator to tell us if the upstream proxy is trusted (or better yet, which IPs should be considered trusted proxies).
  • Implementing X-Forwarded-* for websocket is straightforward; SockJS allows these headers through. In lib/proxy/sockjs.js, createWebSocketClient can accept headers as its second argument, and you can get at the request headers via conn.$conn._conn.headers (obviously we should add accessors instead of reading private fields).

Any update on this? Exposing the request IP inside shiny would be quite useful.

Having access to the client's IP address would be really useful in my projects as well. Any chance we'll see this feature in an upcoming release?

If your app is deployed using Connect or shinyapps.io, there's a bit of a hacky way to do this. We're working on making this less hacky by making it accessible from the server-side (and at all in shiny server), so this is not the final word on the topic. I just wanted to share what does work today if case it's useful for any of you:

library(shiny)

ui_xfwd <- NULL

ui <- function(req) {
...

Is there a way to access the request information (even in a hacky way) from within a Shiny Module?

Is there a way to access the request information (even in a hacky way) from within a Shiny Module?

The approach suggested by @jcheng5 (https://github.com/rstudio/shiny/issues/141#issuecomment-351857670) should work from within a module (once it's fully supported)

I got the solution by @jcheng5 (https://github.com/rstudio/shiny/issues/141#issuecomment-352564869) to work by passing the request environment (req in his code) through to the sub-module from the main ui function.

But now I'm seeing a value of 127.0.0.1, even when accessed from a different machine. Is this expected?

It seems that it is NOT expected, but it sometimes happens to users: https://groups.google.com/d/msg/shiny-discuss/9WcbS3E4Cfc/9hRS6VDyTxYJ

Does someone knows how to apply this solution using a apache reverse proxy?

If I run this solution on the RStudio server installed on my VM it works fine, but when I pass it to production, where I have a reverse proxy implemented, the IP is always 127.0.0.1

Was this page helpful?
0 / 5 - 0 ratings