Skip to content

Commit

Permalink
Decompression exploit check (h2non#404)
Browse files Browse the repository at this point in the history
* Bump bimg version to 1.1.7

* Add decompression bomb exploit check

* Update README with new flag

* Fix tests
  • Loading branch information
SeaaaaaSharp authored and gkmw committed Jun 19, 2024
1 parent 4e69676 commit a88f638
Show file tree
Hide file tree
Showing 10 changed files with 39 additions and 19 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Options:
-url-signature-key The URL signature key (32 characters minimum)
-allowed-origins <urls> Restrict remote image source processing to certain origins (separated by commas). Note: Origins are validated against host *AND* path.
-max-allowed-size <bytes> Restrict maximum size of http image source (in bytes)
-max-allowed-resolution <megapixels> Restrict maximum resolution of the image [default: 18.0]
-certfile <path> TLS certificate file path
-keyfile <path> TLS private key file path
-authorization <value> Defines a constant Authorization header value passed to all the image source servers. -enable-url-source flag must be defined. This overwrites authorization headers forwarding behavior via X-Forward-Authorization
Expand Down
15 changes: 15 additions & 0 deletions controllers.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ func imageHandler(w http.ResponseWriter, r *http.Request, buf []byte, operation
return
}

sizeInfo, err := bimg.Size(buf)

if err != nil {
ErrorReply(r, w, NewError("Error while processing the image: "+err.Error(), http.StatusBadRequest), o)
return
}

// https://en.wikipedia.org/wiki/Image_resolution#Pixel_count
imgResolution := float64(sizeInfo.Width) * float64(sizeInfo.Height)

if (imgResolution / 1000000) > o.MaxAllowedPixels {
ErrorReply(r, w, ErrResolutionTooBig, o)
return
}

image, err := operation.Run(buf, opts)
if err != nil {
// Ensure the Vary header is set when an error occurs
Expand Down
1 change: 1 addition & 0 deletions error.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ var (
ErrNotImplemented = NewError("Not implemented endpoint", http.StatusNotImplemented)
ErrInvalidURLSignature = NewError("Invalid URL signature", http.StatusBadRequest)
ErrURLSignatureMismatch = NewError("URL signature mismatch", http.StatusForbidden)
ErrResolutionTooBig = NewError("Image resolution is too big", http.StatusUnprocessableEntity)
)

type Error struct {
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ go 1.12

require (
github.com/garyburd/redigo v1.6.0 // indirect
github.com/h2non/bimg v1.1.7
github.com/h2non/filetype v1.1.0
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad // indirect
github.com/rs/cors v0.0.0-20170727213201-7af7a1e09ba3
github.com/h2non/bimg v1.1.4
github.com/h2non/filetype v1.1.0
gopkg.in/throttled/throttled.v2 v2.0.3
)
7 changes: 3 additions & 4 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
github.com/garyburd/redigo v1.6.0 h1:0VruCpn7yAIIu7pWVClQC8wxCJEcG3nyzpMSHKi1PQc=
github.com/garyburd/redigo v1.6.0/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
github.com/h2non/bimg v1.1.2 h1:J75W2eM5FT0KjcwsL2aiy1Ilu0Xy0ENb0sU+HHUJAvw=
github.com/h2non/bimg v1.1.2/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/h2non/bimg v1.1.4 h1:6qf7qDo3d9axbNUOcSoQmzleBCMTcQ1PwF3FgGhX4O0=
github.com/h2non/bimg v1.1.4/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/h2non/bimg v1.1.7 h1:JKJe70nDNMWp2wFnTLMGB8qJWQQMaKRn56uHmC/4+34=
github.com/h2non/bimg v1.1.7/go.mod h1:R3+UiYwkK4rQl6KVFTOFJHitgLbZXBZNFh2cv3AEbp8=
github.com/h2non/filetype v1.1.0 h1:Or/gjocJrJRNK/Cri/TDEKFjAR+cfG6eK65NGYB6gBA=
github.com/h2non/filetype v1.1.0/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/golang-lru v0.0.0-20160813221303-0a025b7e63ad h1:eMxs9EL0PvIGS9TTtxg4R+JxuPGav82J8rA+GFnY7po=
Expand Down
2 changes: 1 addition & 1 deletion image.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import (
"errors"
"fmt"
"io"
"strings"
"io/ioutil"
"math"
"net/http"
"strings"

"github.com/h2non/bimg"
)
Expand Down
3 changes: 3 additions & 0 deletions imaginary.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
aURLSignatureKey = flag.String("url-signature-key", "", "The URL signature key (32 characters minimum)")
aAllowedOrigins = flag.String("allowed-origins", "", "Restrict remote image source processing to certain origins (separated by commas). Note: Origins are validated against host *AND* path.")
aMaxAllowedSize = flag.Int("max-allowed-size", 0, "Restrict maximum size of http image source (in bytes)")
aMaxAllowedPixels = flag.Float64("max-allowed-resolution", 18.0, "Restrict maximum resolution of the image (in megapixels)")
aKey = flag.String("key", "", "Define API key for authorization")
aMount = flag.String("mount", "", "Mount server local directory")
aCertFile = flag.String("certfile", "", "TLS certificate file path")
Expand Down Expand Up @@ -95,6 +96,7 @@ Options:
-url-signature-key The URL signature key (32 characters minimum)
-allowed-origins <urls> Restrict remote image source processing to certain origins (separated by commas)
-max-allowed-size <bytes> Restrict maximum size of http image source (in bytes)
-max-allowed-resolution <megapixels> Restrict maximum resolution of the image [default: 18.0]
-certfile <path> TLS certificate file path
-keyfile <path> TLS private key file path
-authorization <value> Defines a constant Authorization header value passed to all the image source servers. -enable-url-source flag must be defined. This overwrites authorization headers forwarding behavior via X-Forward-Authorization
Expand Down Expand Up @@ -158,6 +160,7 @@ func main() {
ForwardHeaders: parseForwardHeaders(*aForwardHeaders),
AllowedOrigins: parseOrigins(*aAllowedOrigins),
MaxAllowedSize: *aMaxAllowedSize,
MaxAllowedPixels: *aMaxAllowedPixels,
LogLevel: getLogLevel(*aLogLevel),
ReturnSize: *aReturnSize,
}
Expand Down
8 changes: 4 additions & 4 deletions log.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ func (r *LogRecord) WriteHeader(status int) {

// LogHandler maps the HTTP handler with a custom io.Writer compatible stream
type LogHandler struct {
handler http.Handler
io io.Writer
logLevel string
handler http.Handler
io io.Writer
logLevel string
}

// NewLog creates a new logger
Expand Down Expand Up @@ -79,7 +79,7 @@ func (h *LogHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
record.time = finishTime.UTC()
record.elapsedTime = finishTime.Sub(startTime)

switch h.logLevel{
switch h.logLevel {
case "error":
if record.status >= http.StatusInternalServerError {
record.Log(h.io)
Expand Down
5 changes: 3 additions & 2 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ package main

import (
"context"
"log"
"net/http"
"net/url"
"log"
"os"
"os/signal"
"syscall"
"path"
"strconv"
"strings"
"syscall"
"time"
)

Expand All @@ -22,6 +22,7 @@ type ServerOptions struct {
HTTPReadTimeout int
HTTPWriteTimeout int
MaxAllowedSize int
MaxAllowedPixels float64
CORS bool
Gzip bool // deprecated
AuthForwarding bool
Expand Down
12 changes: 6 additions & 6 deletions server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import (
)

func TestIndex(t *testing.T) {
opts := ServerOptions{PathPrefix: "/"}
opts := ServerOptions{PathPrefix: "/", MaxAllowedPixels: 18.0}
ts := testServer(indexController(opts))
defer ts.Close()

Expand Down Expand Up @@ -275,7 +275,7 @@ func TestFit(t *testing.T) {
}

func TestRemoteHTTPSource(t *testing.T) {
opts := ServerOptions{EnableURLSource: true}
opts := ServerOptions{EnableURLSource: true, MaxAllowedPixels: 18.0}
fn := ImageMiddleware(opts)(Crop)
LoadSources(opts)

Expand Down Expand Up @@ -316,7 +316,7 @@ func TestRemoteHTTPSource(t *testing.T) {
}

func TestInvalidRemoteHTTPSource(t *testing.T) {
opts := ServerOptions{EnableURLSource: true}
opts := ServerOptions{EnableURLSource: true, MaxAllowedPixels: 18.0}
fn := ImageMiddleware(opts)(Crop)
LoadSources(opts)

Expand All @@ -339,7 +339,7 @@ func TestInvalidRemoteHTTPSource(t *testing.T) {
}

func TestMountDirectory(t *testing.T) {
opts := ServerOptions{Mount: "testdata"}
opts := ServerOptions{Mount: "testdata", MaxAllowedPixels: 18.0}
fn := ImageMiddleware(opts)(Crop)
LoadSources(opts)

Expand Down Expand Up @@ -374,7 +374,7 @@ func TestMountDirectory(t *testing.T) {
}

func TestMountInvalidDirectory(t *testing.T) {
fn := ImageMiddleware(ServerOptions{Mount: "_invalid_"})(Crop)
fn := ImageMiddleware(ServerOptions{Mount: "_invalid_", MaxAllowedPixels: 18.0})(Crop)
ts := httptest.NewServer(fn)
url := ts.URL + "?top=100&left=100&areawidth=200&areaheight=120&file=large.jpg"
defer ts.Close()
Expand Down Expand Up @@ -408,7 +408,7 @@ func TestMountInvalidPath(t *testing.T) {
func controller(op Operation) func(w http.ResponseWriter, r *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
buf, _ := ioutil.ReadAll(r.Body)
imageHandler(w, r, buf, op, ServerOptions{})
imageHandler(w, r, buf, op, ServerOptions{MaxAllowedPixels: 18.0})
}
}

Expand Down

0 comments on commit a88f638

Please sign in to comment.