Skip to content

Instantly share code, notes, and snippets.

@tyndyll
Last active June 28, 2017 21:11
Show Gist options
  • Save tyndyll/c7339e4828f620c7848e4ad761249984 to your computer and use it in GitHub Desktop.
Save tyndyll/c7339e4828f620c7848e4ad761249984 to your computer and use it in GitHub Desktop.
HTTP Referer Handler for Files

Simple Go App to Serve HTTP REFERER Protected Files

This is a simple Go app that will serve files when the scheme and host in the referer of the request matches that of the configured referer in the application. This referer is configured via the environmental variable HOST_REFERER. The port is statically configured to 8000. The

The server serves files out of a directory files in the same directory as from main.go

For convenience the server can be build using Docker. In this case the files directory must exist when the container is built. If the contents of the files directory is changed then the container must be built again. This can be mitigated by using the VOLUMES available in Docker

Operation

# Build the docker container. Requires docker to be installed
docker build -t fntsrv .
Sending build context to Docker daemon 5.632 kB
Step 1/7 : FROM golang:latest
 ---> d2f558dda133
Step 2/7 : EXPOSE 8000
 ---> Using cache
 ---> 8146f7f21305
Step 3/7 : ENV HOST_REFERER ""
 ---> Using cache
 ---> be652b2bde81
Step 4/7 : WORKDIR /go/src/app
 ---> Using cache
 ---> 7cb5b1078bec
Step 5/7 : COPY . .
 ---> b1ad59f46b9b
Removing intermediate container 35f2029bf8df
Step 6/7 : RUN go-wrapper install    # "go install -v ./..."
 ---> Running in f56a86f8a26f
+ exec go install -v
app
 ---> 153cae4bee6d
Removing intermediate container f56a86f8a26f
Step 7/7 : CMD go-wrapper run
 ---> Running in 9a4264be5fea
 ---> 8065accb0074
Removing intermediate container 9a4264be5fea
Successfully built 8065accb0074



# Run the container
docker run -it -e HOST_REFERER="http://google.com" --rm -p 8000:8000 fntsrv
+ exec app

Testing

# Using cURL
curl --referer http://example.com/robots.txt -vv http://localhost:8000/main.go
*   Trying ::1...
* Connected to localhost (::1) port 8000 (#0)
> GET /main.go HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.43.0
> Accept: */*
> Referer: http://example.com/robots.txt
>
< HTTP/1.1 403 Forbidden
< Content-Type: text/plain; charset=utf-8
< X-Content-Type-Options: nosniff
< Date: Wed, 28 Jun 2017 21:04:01 GMT
< Content-Length: 16
<
Invalid Referer

curl --referer http://google.com/robots.txt -vv http://localhost:8000/main.go
*   Trying ::1...
* Connected to localhost (::1) port 8000 (#0)
> GET /main.go HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.43.0
> Accept: */*
> Referer: http://google.com/robots.txt
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Content-Length: 308
< Content-Type: text/plain; charset=utf-8
< Last-Modified: Tue, 27 Jun 2017 12:11:09 GMT
< Date: Wed, 28 Jun 2017 21:04:23 GMT
<
...... OUTPUTS MAIN.GO
FROM golang:latest
EXPOSE 8000
ENV HOST_REFERER ""
WORKDIR /go/src/app
COPY . .
RUN go-wrapper install # "go install -v ./..."
CMD ["go-wrapper", "run"] # ["app"]
package main
import (
"fmt"
"log"
"net/http"
"net/url"
"os"
)
const (
hostRefererEnv = "HOST_REFERER"
)
func serveFile(w http.ResponseWriter, req *http.Request) {
if referer, err := url.ParseRequestURI(req.Referer()); err != nil {
log.Println("Referer is not a URL: ", req.Referer())
http.Error(w, `Invalid Referer`, http.StatusBadRequest)
return
} else {
if fmt.Sprintf("%s://%s", referer.Scheme, referer.Host) != os.Getenv(hostRefererEnv) {
log.Println("Invalid referer", req.Referer())
http.Error(w, `Invalid Referer`, http.StatusForbidden)
return
}
}
if req.URL.Path == "/" {
log.Println("Cannot access root path")
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
return
}
file := fmt.Sprintf(`files/%s`, req.URL.Path)
log.Printf("Attempting to access: %s \n", req.URL.Path)
http.ServeFile(w, req, file)
}
func main() {
if os.Getenv("hostRefererEnv") == "/" {
log.Fatalln(fmt.Sprintf("%s must be set to expected referer", hostRefererEnv))
}
if _, err := url.ParseRequestURI(os.Getenv(hostRefererEnv)); err != nil {
log.Fatalln(fmt.Sprintf("%s must be set to a valid URL", hostRefererEnv))
}
http.HandleFunc("/", serveFile)
log.Fatalln(http.ListenAndServe(":8000", nil))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment