goでHTTPリクエストボディのサイズを制限する「MaxBytesReader」
webアプリケーションを作成していると、ユーザが誤って、もしくは悪意を持って巨大なリクエストを送ってくる場合があります。 何も対応しないとリクエストを受け付けてしまい、アプリケーションが不安定になったり無駄にメモリを確保してしまいます。
そこで今回はgoでHTTPのリクエストボディを制限するMaxBytesReaderを紹介します。
MaxBytesReaderとは?
MaxBytesReader prevents clients from accidentally or maliciously sending a large request and wasting server resources.
MaxBytesReader は io.LimitReader に似ていますが、受信するリクエスト ボディのサイズを制限することを目的としています。 io.LimitReader とは対照的に、MaxBytesReader の結果は ReadCloser であり、制限を超えた読み取りに対しては非 EOF エラーを返し、 Close メソッドが呼び出されると基礎となるリーダーを閉じます。
MaxBytesReaderは、クライアントが誤ってまたは悪意を持って大きなリクエストを送信し、サーバーのリソースを浪費することを防ぎます。MaxBytesReader
関数の定義は以下のようになっています。
func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser
使い方
以下の例ではmyHandler関数内でリクエストのBodyをバイト数10で指定してMaxBytesReaderでWrapしています。
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"net/http/httptest"
)
func main() {
reqBody := bytes.NewBufferString("abcdefghijklmnopqrstuvwxyz")
req := httptest.NewRequest(http.MethodGet, "http://dummy.url.com/", reqBody)
got := httptest.NewRecorder()
myHandler(got, req)
fmt.Println(got.Body.String())
}
func myHandler(w http.ResponseWriter, r *http.Request) {
body := http.MaxBytesReader(w, r.Body, 10)
i, err := io.Copy(w, body)
fmt.Println(i)
if err != nil {
fmt.Println(err)
}
}
上記を実行すると以下の出力が得られます。
$ go run sample.go
10
http: request body too large
abcdefghij
io.Copyで書き出した数が10となっており、指定したバイト数と一致しています。
また、errには http: request body too large というメッセージが入っています。これはMaxBytesReaderの定型エラーメッセージです。
最後にmain関数側で読み込んだ10バイト分が出力されています。
ginで利用する場合
先ほどの例ではハンドラー毎に制限をかける必要があるのでginで一括でリクエストに対して制限をかける例を紹介します。
以下のサンプルはPOSTで受けたJSONリクエストをmap[string]string{}にバインドしてその値をまたJSON形式に変換して返します。 リクエストのサイズ制限は10バイトとして定義しています。
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
const (
MaxBodyBytes = 10
)
func main() {
router := gin.Default()
router.Use(bodySizeMiddleware)
router.POST("/", func(ctx *gin.Context) {
m := map[string]string{}
if err := ctx.BindJSON(&m); err != nil {
ctx.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
"message": err.Error(),
})
return
}
ctx.JSON(200, m)
})
router.Run()
}
func bodySizeMiddleware(c *gin.Context) {
var w http.ResponseWriter = c.Writer
c.Request.Body = http.MaxBytesReader(w, c.Request.Body, MaxBodyBytes)
c.Next()
}
ginのミドルウェアの機構を利用してリクエストがあった場合に事前にRequest.Bodyを MaxBytesReader でWrapしておいて、 実際の読みだし(このケースの場合は ctx.BindJSON )でエラーを取得しています。
このサンプルを実行して10バイト以上のデータを送ると以下のエラーが返されます。
{"message":"http: request body too large"}{}
さいごに
いかがだったでしょうか。 関数が用意されているので意外と簡単にリクエストのサイズを制限する事ができました。 実際に運用する場合はwebサーバ側やContent-Lengthなど他の制限手段と併せてリクエストに制限を加えるのがいいと思います。
この記事を書いた人
- 創造性を最大限に発揮するとともに、インターネットに代表されるITを活用し、みんなの生活が便利で、豊かで、楽しいものになるようなサービスやコンテンツを考え、創り出し提供しています。
この執筆者の最新記事
関連記事
最新記事
FOLLOW US
最新の情報をお届けします
- facebookでフォロー
- Twitterでフォロー
- Feedlyでフォロー