0

I have a golang api I'm writting. I use the following function for cors

func ResponseWithJSON(w http.ResponseWriter, json []byte, code int) {
    w.Header().Set("Content-Type", "application/json; charset=utf-8")
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.WriteHeader(code)
    w.Write(json)
}

This allows my api to be accessible by anyone. I would like to limit it to my domain name. Because that sounds more secure. Lets call it www.example.com

I can Change it to

 w.Header().Set("Access-Control-Allow-Origin", "http://www.example.com")

And this will allow me to make calls when the url is www.example.com but not example.com

I can then change it to

w.Header().Set("Access-Control-Allow-Origin", "http://example.com")

Now I can access my api from example.com but not www.example.com

Adding both does not work Neither this way

w.Header().Set("Access-Control-Allow-Origin", "http://www.example.com,http://example.com")

Nor This way

w.Header().Set("Access-Control-Allow-Origin", "http://www.example.com")
w.Header().Set("Access-Control-Allow-Origin", "http://example.com")

So, is there a way for me to get the requesting origin? so I can dynamically allow the domain? Is there another way for me to solve this problem?

camccar
  • 690
  • 12
  • 24
  • 2
    Check the request `host` header and serve the response correspondingly. – zerkms Nov 15 '17 at 02:23
  • How do I do that? Do you have any examples? – camccar Nov 15 '17 at 02:31
  • https://stackoverflow.com/q/42921567/251311 – zerkms Nov 15 '17 at 02:33
  • 1
    FWIW, setting the same header multiple times won’t work with [`Header.Set()`](https://golang.org/pkg/net/http/#Header.Set) as it replaces any previously set values. Instead use [`Header.Add()`](https://golang.org/pkg/net/http/#Header.Add). – Andrew Marshall Nov 15 '17 at 02:39
  • No that doesn't work. Now it says The 'Access-Control-Allow-Origin' header contains multiple values – camccar Nov 15 '17 at 03:10
  • If you want both try this: `w.Header().Set("Access-Control-Allow-Origin", "http://www.example.com http://example.com")` - space as separator instead of a comma. According to [this](https://www.w3.org/TR/cors/#access-control-allow-origin-response-header) the list is space separated. – k1m190r Nov 15 '17 at 14:40
  • 1
    Apparently 'Neither comma separated, nor space separated domains did work. Matching against a list of domains and putting a single host in the headers is still better security and does work properly.' So back to @zerkms comments. see [here](https://stackoverflow.com/questions/25309318/best-method-access-control-allow-origin-multiple-origin-domains) – k1m190r Nov 15 '17 at 14:48
  • Yeah it still complains about multiple headers. I feel like that would be the ideal sollution. I did finally figure it out this morning. The information I want is in the http.request object earlier in the code. Assuming you have an object r *http.Request, you can then get the origin like this origin := r.Header.Get("Origin"); So I'm just returning an empty object on each api call if it isn't the correct origin. – camccar Nov 15 '17 at 14:55

2 Answers2

3

I found that the Origin information is in the http.Request object. You can get the origin with

origin := r.Header.Get("Origin");

Assuming you have a object some where like

r *http.Request

If the object is coming from example.com it will return example.com, likewise www.example.com. You can then test if it is one of these two values as a way to authenticate.

camccar
  • 690
  • 12
  • 24
1

The Access-Control-Allow-Origin header supports only a single value, so you have to inspect the Host request header and then make a decision based on that:

package main

import "net/http"

func myHandler(w http.ResponseWriter, r *http.Request) {
    switch host := r.Header.Get("Host"); host {
    case "www.example.com", "example.com":
            w.Header().Set("Access-Control-Allow-Origin", "http://"+host)
    }
}

Not that the Host header is sometimes changed by proxy servers. They then usually add an X-Forwarded-Host header or similar. Only inspect that if you trust the proxy, though (and how to establish that trust is a different question).

Peter
  • 29,454
  • 5
  • 48
  • 60
  • 2
    It's r.Header.Get("Origin") Not Host – camccar Nov 15 '17 at 15:18
  • Those headers serve different purposes. See [RFC7230](https://tools.ietf.org/html/rfc7230#section-5.4) for a description of the Host header. – Peter Nov 15 '17 at 15:23
  • I just tested it and Host is returning an empty string, origin is returning what I asked for, the origin... – camccar Nov 15 '17 at 15:28
  • Host is the domain to which the request is being sent to so I don't think it should be set back in the `Access-Control-Allow-Origin`. "Origin" is the correct one and we can also omit the `http://`. – Krishnaraj Aug 05 '21 at 19:25