親バカエンジニアのナレッジ帳

webのエンジニアをやっており、日頃の開発で詰まったことや書き残しておきたいことを載せています。

Go+GinでCors設定を行い、クロスオリジンのアクセスを制御する

APIとして使用される場合を想定

近年SPAサイトがよく作られており、サーバサイドの言語はAPIとして開発されることが多いでしょう。
APIとして使用する場合、注意しなくてはいけないのがクロスオリジンの設定です。
他サイトから自由自在にアクセスされてしまえば、セキュリティ的にアウトですしDos攻撃の餌食にもなってしまいます。
なので、APIへのアクセスを許可するサイトURL、メソッド(POSTやGET)、ヘッダー情報を予め設定しておきましょう。

GinのGors設定

まずは必要なモジュールをインストールします。

go get github.com/gin-contrib/cors

あとは以下のように「r.Use(cors.New(cors.Config{}...」と設定値を入力するだけで良いのです。
以下はあくまでサンプルですが、内容についてはコメントを入れていきます。

package main

import (
  "github.com/gin-gonic/gin"
  "github.com/gin-contrib/cors"
  "time"
)

func main() {

  r := gin.Default()

  // ここからCorsの設定
  r.Use(cors.New(cors.Config{
    // アクセスを許可したいアクセス元
    AllowOrigins: []string{
        'https://example.com',
        'https://example2.com',
    },
    // アクセスを許可したいHTTPメソッド(以下の例だとPUTやDELETEはアクセスできません)
    AllowMethods: []string{
        "POST",
        "GET",
        "OPTIONS",
    },
    // 許可したいHTTPリクエストヘッダ
    AllowHeaders: []string{
        "Access-Control-Allow-Credentials",
        "Access-Control-Allow-Headers",
        "Content-Type",
        "Content-Length",
        "Accept-Encoding",
        "Authorization",
    },
    // cookieなどの情報を必要とするかどうか
    AllowCredentials: true
    // preflightリクエストの結果をキャッシュする時間
    MaxAge: 24 * time.Hour,
  }))

  r.GET("/", func(c *gin.Context) {
    c.String(200, "Hello,World!")
  })
  r.POST("/api/test", controller.TestMethod)

  r.Run(":9000")
}

上記以外にも様々な設定情報があるので、詳細は以下をご覧ください。
https://godoc.org/github.com/gin-contrib/cors#Config

許可されないアクセスがされた場合

ではアクセスが許可されていないサイトからアクセスされた場合、どのようなレスポンスが返ってくるのでしょうか。
実際にcurlコマンドでアクセスをして色々試してもらいたいのですが、403が返ってきます。(gin-contrib/corsの仕様)
エラーコード403の意味はご存知ですか?
はい、アクセス禁止という意味です。
不正なアクセスに対してはしっかりブロックできていますね。

CORS 対応の後にルーティングを書かないとうまく動かない

注意点ですが、以下のようにルーティングをCors設定の前に書かないように注意してください。

package main

import (
  "github.com/gin-gonic/gin"
  "github.com/gin-contrib/cors"
  "time"
)

func main() {

  r := gin.Default()

  // ルーティングがCors設定の前に書かれている
  r.GET("/", func(c *gin.Context) {
    c.String(200, "Hello,World!")
  })
  r.POST("/api/test", controller.TestMethod)

  r.Use(cors.New(cors.Config{
    AllowOrigins: []string{
        'https://example.com',
        'https://example2.com',
    },
    AllowMethods: []string{
        "POST",
        "GET",
        "OPTIONS",
    },
    AllowHeaders: []string{
        "Access-Control-Allow-Credentials",
        "Access-Control-Allow-Headers",
        "Content-Type",
        "Content-Length",
        "Accept-Encoding",
        "Authorization",
    },
    AllowCredentials: true
    MaxAge: 24 * time.Hour,
  }))

  r.Run(":9000")
}

これだとCorsの制御に入る前にルーティング先に遷移してしまうためか上手くいきません。
必ずルーティングの前にCorsの制御を書くように注意してください。