shiny icon indicating copy to clipboard operation
shiny copied to clipboard

Make client IP address available on clientData

Open jcheng5 opened this issue 12 years ago • 27 comments
trafficstars

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

jcheng5 avatar Apr 05 '13 18:04 jcheng5

Also HTTP request headers.

jcheng5 avatar Apr 09 '13 21:04 jcheng5

http://rod.vagg.org/2011/07/wrangling-the-x-forwarded-for-header/

jcheng5 avatar Apr 09 '13 21:04 jcheng5

Has any movement happened on this? @jcheng5

coatless avatar Apr 01 '16 06:04 coatless

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

jcheng5 avatar Apr 04 '16 06:04 jcheng5

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.

coatless avatar Apr 04 '16 17:04 coatless

Another vote for exposing request IP

daattali avatar Aug 16 '17 16:08 daattali

Yet another vote.

rickn-horizon avatar Oct 26 '17 12:10 rickn-horizon

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)

bborgesr avatar Oct 31 '17 20:10 bborgesr

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

jcheng5 avatar Oct 31 '17 22:10 jcheng5

@jcheng5 any eta on the robust method?

coatless avatar Nov 19 '17 06:11 coatless

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 avatar Dec 14 '17 22:12 jcheng5

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

coatless avatar Dec 15 '17 00:12 coatless

@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 avatar Dec 15 '17 17:12 jcheng5

@jcheng5 excellent. A thousand thanks mate!

coatless avatar Dec 15 '17 18:12 coatless

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.

jcheng5 avatar Dec 15 '17 19:12 jcheng5

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).

jcheng5 avatar Dec 18 '17 21:12 jcheng5

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

Chrisss93 avatar May 23 '18 14:05 Chrisss93

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?

dansmith01 avatar Aug 30 '18 05:08 dansmith01

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?

yoderj avatar Jun 24 '19 17:06 yoderj

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)

cpsievert avatar Jun 24 '19 18:06 cpsievert

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

yoderj avatar Jun 24 '19 18:06 yoderj

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

silvano-junior avatar Mar 07 '20 21:03 silvano-junior

Any update on this? It would be great to be able to get clients' IP addresses to catch ineligible or fraudulent responses.

isaactpetersen avatar Apr 14 '21 03:04 isaactpetersen

Has anyone tried session$req$HTTP_X_FORWARDED_FOR on shinyapps.io recently?

jcheng5 avatar Apr 14 '21 03:04 jcheng5

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.

Thanks @jcheng5 for this. I want to try to implement this in my shiny application. I've a just a quick question. What do you mean for "worried about fraud"? Is adding this function problematic on the users or developer side?

Thank you for your availability. I'll write back here as soon as I have a working example.

nicocriscuolo avatar Jun 04 '21 13:06 nicocriscuolo

Are there any updates on this?

jcvdav avatar Sep 03 '23 22:09 jcvdav

I'm another user of the open source server (https://shiny.psyctc.org/) and am on essentially zero budget providing free apps so I'm never going to be able to afford shiny server pro sadly. I would really like to see making the x-forwarded-for header available in the open source server (and ideally some guide as to how to process it!) Is it such a huge programming challenge?! TIA (and thanks for an amazing open source server though I do crave this!)

cpsyctc2 avatar Jun 19 '24 16:06 cpsyctc2