Skip to content

leg100/surl

Repository files navigation

Go Reference

surl

Create signed URLs in Go.

Installation

go get github.com/leg100/surl/v2@latest

Usage

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/leg100/surl/v2"
)

func main() {
	signer := surl.New([]byte("secret_key"))

	// Create a signed URL that expires in one hour.
	signed, _ := signer.Sign("https://example.com/a/b/c?foo=bar", time.Now().Add(time.Hour))
	fmt.Println(signed)
	// Outputs something like:
	// https://example.com/a/b/c?expiry=1667331055&foo=bar&signature=TGvxmRwpoAUt9YEIbeJ164lMYrzA2DBnYB9Lcy9m1T

	err := signer.Verify(signed)
	if err != nil {
		log.Fatal("verification failed: ", err.Error())
	}
	fmt.Println("verification succeeded")
}

Options

The format and behaviour of signed URLs can be configured by passing options to the constructor.

Query Formatter

surl.New(secret, surl.WithQueryFormatter())

The query formatter is the default format. It stores the signature and expiry in query parameters:

https://example.com/a/b/c?expiry=1667331055&foo=bar&signature=TGvxmRwpoAUt9YEIbeJ164lMYrzA2DBnYB9Lcy9m1T

Path Formatter

surl.New(secret, surl.WithPathFormatter())

The path formatter stores the signature and expiry in the path itself:

https://example.com/PaMIbZQ6wxPdHXVLfIGwZBULo-FSTdt7-bCLZjBPPUE.1669574162/a/b/c?foo=bar

Prefix Path

surl.New(secret, surl.PrefixPath("/signed"))

Prefix the signed URL path:

https://example.com/signed/a/b/c?expiry=1669574398&foo=bar&signature=NvIrIFcc1OaKgeVSN685tSD26PTdjlUxxSZRE18Wk_8

Note: a slash is implicitly inserted between the prefix and the rest of the path.

Skip Query

surl.New(secret, surl.SkipQuery())

Skip the query string when computing the signature. This is useful, say, if you have pagination query parameters but you want to use the same signed URL regardless of their value. See the example.

Skip Scheme

surl.New(secret, surl.SkipScheme())

Skip the scheme when computing the signature. This is useful, say, if you generate signed URLs in production where you use https but you want to use these URLs in development too where you use http. See the example.

Decimal Encoding of Expiry

surl.New(secret, surl.WithDecimalExpiry())

Encode expiry in decimal. This is the default.

https://example.com/a/b/c?expiry=1667331055&foo=bar&signature=TGvxmRwpoAUt9YEIbeJ164lMYrzA2DBnYB9Lcy9m1T

Base58 Encoding of Expiry

surl.New(secret, surl.WithBase58Expiry())

Encode the expiry using Base58:

https://example.com/a/b/c?expiry=3xx1vi&foo=bar&signature=-mwCtMLTBgDkShZTbBcHjRCRXtO_ZYPE0cmrh3u6S-s

Notes

  • Any change in the order of the query parameters in a signed URL renders it invalid, unless SkipQuery is specified.

Benchmarks

> go test -run=XXX -bench=.
goos: linux
goarch: amd64
pkg: github.com/leg100/surl
cpu: AMD Ryzen 7 3800X 8-Core Processor             
Benchmark/sign/path/decimal/no_opts-16            790069              1366 ns/op
Benchmark/verify/path/decimal/no_opts-16          865004              1267 ns/op
Benchmark/sign/path/decimal/prefix-16             671443              1659 ns/op
Benchmark/verify/path/decimal/prefix-16           836241              1303 ns/op
Benchmark/sign/path/decimal/skip_query-16         813769              1354 ns/op
Benchmark/verify/path/decimal/skip_query-16       869764              1262 ns/op
Benchmark/sign/path/decimal/skip_scheme-16        799594              1351 ns/op
Benchmark/verify/path/decimal/skip_scheme-16      865023              1262 ns/op
Benchmark/sign/path/decimal/prefix_and_skip_query-16              681868              1641 ns/op
Benchmark/verify/path/decimal/prefix_and_skip_query-16            835401              1288 ns/op
Benchmark/sign/path/decimal/prefix_and_skip_scheme-16             671688              1654 ns/op
Benchmark/verify/path/decimal/prefix_and_skip_scheme-16           837308              1285 ns/op
Benchmark/sign/path/decimal/prefix_and_skip_query_and_skip_scheme-16              677064              1645 ns/op
Benchmark/verify/path/decimal/prefix_and_skip_query_and_skip_scheme-16            859400              1283 ns/op
Benchmark/sign/path/base58/no_opts-16                                             832389              1332 ns/op
Benchmark/verify/path/base58/no_opts-16                                           894231              1238 ns/op
Benchmark/sign/path/base58/prefix-16                                              678120              1627 ns/op
Benchmark/verify/path/base58/prefix-16                                            832461              1280 ns/op
Benchmark/sign/path/base58/skip_query-16                                          829882              1307 ns/op
Benchmark/verify/path/base58/skip_query-16                                        903090              1218 ns/op
Benchmark/sign/path/base58/skip_scheme-16                                         785352              1322 ns/op
Benchmark/verify/path/base58/skip_scheme-16                                       886584              1233 ns/op
Benchmark/sign/path/base58/prefix_and_skip_query-16                               691098              1586 ns/op
Benchmark/verify/path/base58/prefix_and_skip_query-16                             866223              1256 ns/op
Benchmark/sign/path/base58/prefix_and_skip_scheme-16                              679152              1609 ns/op
Benchmark/verify/path/base58/prefix_and_skip_scheme-16                            872644              1274 ns/op
Benchmark/sign/path/base58/prefix_and_skip_query_and_skip_scheme-16               675469              1591 ns/op
Benchmark/verify/path/base58/prefix_and_skip_query_and_skip_scheme-16             871072              1237 ns/op
Benchmark/sign/query/decimal/no_opts-16                                           347485              3382 ns/op
Benchmark/verify/query/decimal/no_opts-16                                         367434              3140 ns/op
Benchmark/sign/query/decimal/prefix-16                                            321260              3646 ns/op
Benchmark/verify/query/decimal/prefix-16                                          361286              3153 ns/op
Benchmark/sign/query/decimal/skip_query-16                                        276272              4211 ns/op
Benchmark/verify/query/decimal/skip_query-16                                      293073              3852 ns/op
Benchmark/sign/query/decimal/skip_scheme-16                                       352858              3281 ns/op
Benchmark/verify/query/decimal/skip_scheme-16                                     373916              3035 ns/op
Benchmark/sign/query/decimal/prefix_and_skip_query-16                             267811              4353 ns/op
Benchmark/verify/query/decimal/prefix_and_skip_query-16                           301072              3875 ns/op
Benchmark/sign/query/decimal/prefix_and_skip_scheme-16                            326252              3505 ns/op
Benchmark/verify/query/decimal/prefix_and_skip_scheme-16                          370263              3059 ns/op
Benchmark/sign/query/decimal/prefix_and_skip_query_and_skip_scheme-16             267507              4341 ns/op
Benchmark/verify/query/decimal/prefix_and_skip_query_and_skip_scheme-16           298611              3857 ns/op
Benchmark/sign/query/base58/no_opts-16                                            347785              3244 ns/op
Benchmark/verify/query/base58/no_opts-16                                          376110              3032 ns/op
Benchmark/sign/query/base58/prefix-16                                             324220              3589 ns/op
Benchmark/verify/query/base58/prefix-16                                           331550              3107 ns/op
Benchmark/sign/query/base58/skip_query-16                                         291204              4002 ns/op
Benchmark/verify/query/base58/skip_query-16                                       302330              3834 ns/op
Benchmark/sign/query/base58/skip_scheme-16                                        328934              3349 ns/op
Benchmark/verify/query/base58/skip_scheme-16                                      353527              3185 ns/op
Benchmark/sign/query/base58/prefix_and_skip_query-16                              266244              4366 ns/op
Benchmark/verify/query/base58/prefix_and_skip_query-16                            300997              3846 ns/op
Benchmark/sign/query/base58/prefix_and_skip_scheme-16                             328764              3506 ns/op
Benchmark/verify/query/base58/prefix_and_skip_scheme-16                           378475              3174 ns/op
Benchmark/sign/query/base58/prefix_and_skip_query_and_skip_scheme-16              268564              4276 ns/op
Benchmark/verify/query/base58/prefix_and_skip_query_and_skip_scheme-16            295378              3939 ns/op
PASS
ok      github.com/leg100/surl  64.305s