diff --git a/.github/workflows/lint.yaml b/.github/workflows/lint.yaml
index d26836f6df073f4d7f062b7b3c843350a6ed8e00..315e7fe220bd3c51196940a07e606391c734c099 100644
--- a/.github/workflows/lint.yaml
+++ b/.github/workflows/lint.yaml
@@ -14,7 +14,7 @@ jobs:
       - uses: actions/checkout@v2
       - uses: actions/setup-go@v2
         with:
-          go-version: '1.19'
-      - uses: golangci/golangci-lint-action@v3.2.0
+          go-version: '1.20'
+      - uses: golangci/golangci-lint-action@v3.4.0
         with:
-          version: v1.50.0
+          version: v1.51.1
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index a0fac4e0cbebe034d8565cae478a8c5c21ec2b40..a1c920c9b24b8b9044a371a27ec783b39abf89db 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -19,7 +19,7 @@ jobs:
     steps:
     - uses: actions/setup-go@v2
       with:
-        go-version: '1.19'
+        go-version: '1.20'
 
     - uses: actions/checkout@v2
 
@@ -38,8 +38,8 @@ jobs:
     strategy:
       matrix:
         go_version:
+        - '1.19'
         - '1.18'
-        - '1.17'
 
     steps:
     - uses: actions/setup-go@v2
diff --git a/Dockerfile b/Dockerfile
index d90249f26cda3897435701af2241089014bffe83..967986ea882060ae3efaaebc278457e56f558c7c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
 # syntax = docker/dockerfile:1.3
-FROM golang:1.19 AS build
+FROM golang:1.20 AS build
 
 WORKDIR /go/src/github.com/mccutchen/go-httpbin
 
diff --git a/httpbin/digest/digest.go b/httpbin/digest/digest.go
index 7497b0f0998714a8f71167eb630bebe6c6b54ffd..9b1e6667d063c7e07c5f20be53524974730f4039 100644
--- a/httpbin/digest/digest.go
+++ b/httpbin/digest/digest.go
@@ -12,10 +12,10 @@ package digest
 
 import (
 	"crypto/md5"
+	crypto_rand "crypto/rand"
 	"crypto/sha256"
 	"crypto/subtle"
 	"fmt"
-	"math/rand"
 	"net/http"
 	"strings"
 	"time"
@@ -55,7 +55,7 @@ func Check(req *http.Request, username, password string) bool {
 // algorithm. If an invalid realm or an unsupported algorithm is given
 func Challenge(realm string, algorithm digestAlgorithm) string {
 	entropy := make([]byte, 32)
-	rand.Read(entropy)
+	crypto_rand.Read(entropy)
 
 	opaqueVal := entropy[:16]
 	nonceVal := fmt.Sprintf("%s:%x", time.Now(), entropy[16:31])
diff --git a/httpbin/handlers_test.go b/httpbin/handlers_test.go
index 07a5e4af9c603a33358f16527842dd0de5d93e3a..f85c42f706cc2452b4a9324b1e5f814753a659af 100644
--- a/httpbin/handlers_test.go
+++ b/httpbin/handlers_test.go
@@ -91,7 +91,7 @@ func assertBodyEquals(t *testing.T, w *httptest.ResponseRecorder, want string) {
 }
 
 func randStringBytes(n int) string {
-	rand.Seed(time.Now().UnixNano())
+	rand.New(rand.NewSource(time.Now().UnixNano()))
 	b := make([]byte, n)
 	for i := range b {
 		b[i] = alphanumLetters[rand.Intn(len(alphanumLetters))]
diff --git a/httpbin/helpers.go b/httpbin/helpers.go
index 705c01079d1bb13d89ac3c92d14f657668cbc5b3..a8bf963db6c1b5129d271e97a613baad6520d0cb 100644
--- a/httpbin/helpers.go
+++ b/httpbin/helpers.go
@@ -2,6 +2,7 @@ package httpbin
 
 import (
 	"bytes"
+	crypto_rand "crypto/rand"
 	"crypto/sha1"
 	"encoding/base64"
 	"encoding/json"
@@ -284,7 +285,7 @@ func sha1hash(input string) string {
 
 func uuidv4() string {
 	buff := make([]byte, 16)
-	_, err := rand.Read(buff[:])
+	_, err := crypto_rand.Read(buff[:])
 	if err != nil {
 		panic(err)
 	}