Security Checklist
for a Go Developer
Elena Grahovac
GopherCon Russia 2019
Video (Russian, original): https://youtu.be/7g90BhAzWtk
Video (English, translated): https://youtu.be/I3O9WOixg28
Security Checklist
for a Go Developer
Elena Grahovac
GopherCon Russia 2019
Video (Russian, original): https://youtu.be/7g90BhAzWtk
Video (English, translated): https://youtu.be/I3O9WOixg28
The role of Security in DevOps
Stage | Examples of Security Input |
Define Way of Working | General Requirements, Policies |
Design | Threat Modelling, Design Guidelines |
Code | Coding Rules, Codereview, Tools |
Test | Testing Plan, Toolkits |
Deploy | Configuration, Checklists |
Monitor | Incident Management, Scanning, Pen Test |
We know that in a world of DevOps it’s critically important to involve security as much as possible. We also know that DevOps is a culture of trust and personal responsibility.
Lead TechOps Automation Engineer @N26
DevOps Enthusiast
hello@grahovac.pro
webdeva
rumyantseva
I’m not a Security Engineer, I’m a Software Developer but I believe that it’s my personal responsibility to make my applications reliable and secure. Which practices, tools and techniques might help me to support requested level of security in my applications?
Security Onion Model
Data
Code
Container
PaaS
Cloud
Dependencies
Let’s consider a cloud service as an onion. These are the layers we can include in such model.
Security Onion Model
Data
Code
Container
Dependencies
As you know, usually, PaaS and Cloud are covered by infrastructure engineers but developers are involved in four top layers of this onion.
Data
Let’s start from Data
Data checklist
Passwords
-- PostgreSQL
UPDATE credentials
SET password = crypt('new-password', gen_salt('bf'));
If we talk about data and security, passwords is probably the first thing which comes to mind.
We know that when we talk about password encryption we can rely on the storage like in this example we rely on the database.
math/rand
package main
import (
"fmt"
"math/rand"
)
func main() {
rand.Seed(1)
b := make([]byte, 4)
_, err := rand.Read(b)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Println(b)
}
But what if we need to generate a random password or key or some other sequence?
math/rand + seed
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
b := make([]byte, 4)
_, err := rand.Read(b)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Println(b)
}
We can seed rand with timestamp but is it good enough? In fact, it’s still deterministic
crypto/rand
package main
import (
"crypto/rand"
"fmt"
)
func main() {
b := make([]byte, 4)
_, err := rand.Read(b)
if err != nil {
fmt.Println("error: ", err)
return
}
fmt.Println(b)
}
Luckily, we have the crypto/rand library which offers a non-deterministic way to generate random sequences.
Secrets
Our applications interact with a lot of sensitive data internally: a database password, or an API key are as much important as user data. As we can see from this example, some people don’t really care about their secrets.
Secrets
...Other people forget to clean their git history
Secrets: Kubernetes case study
So, I mustn’t store my secrets in source code.
FROM busybox
ENV HELLO world
RUN mkdir /www && touch /www/index.html
EXPOSE 8000
CMD httpd -p 8000 -h /www -f & wait
# How to try:
# docker build -t hello .
# docker run hello
# docker exec $(docker ps -q -l) cat /proc/1/environ
Secrets: Solutions
Data
Code
Next layer: code
Code checklist
Validate & sanitize input
It’s pretty straightforward that we should validate and sanitize data from users to prevent SQL injections, XSS attacks or just don’t allow any other data break the application. What developers sometimes forget is that it might make sense to also validate import from “trusted” sources.
Additional examples. XSS
As we mostly use Go for backend, potential XSS attacks are not that often. Anyway, if you build a webpage with Go, don’t forget to choose the right solution (e.g. here we compare text/template and html/template libraries).
Additional examples. SQL injection
Diagnostics handlers
package pprofed
import (
"net/http"
_ "net/http/pprof" // add pprof handlers
)
func serve(addr string) {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
// provide some business logic
})
http.ListenAndServe(addr, nil)
}
Don’t forget to hide your diagnostics handlers!
Diagnostics handlers
…
r.HandleFunc("/diag/pprof", pprof.Index)
r.HandleFunc("/diag/cmdline", pprof.Cmdline)
r.HandleFunc("/diag/profile", pprof.Profile)
r.HandleFunc("/diag/symbol", pprof.Symbol)
r.HandleFunc("/diag/trace", pprof.Trace)
r.Handle("/diag/goroutine", pprof.Handler("goroutine"))
r.Handle("/diag/heap", pprof.Handler("heap"))
r.Handle("/diag/threadcreate", pprof.Handler("threadcreate"))
r.Handle("/diag/block", pprof.Handler("block"))
http.ListenAndServe(addr, r)
…
It’s not really straightforward but actually you can redefine endpoints
Static code analysis: gosec
Results:
[examples/random/math-seed-time/main.go:13] - G404: Use of weak random number
generator (math/rand instead of crypto/rand) (Confidence: MEDIUM, Severity: HIGH)
> rand.Read(b)
[examples/sql-injection/main.go:14-17]
- G201: SQL string formatting (Confidence: HIGH, Severity: MEDIUM)
> fmt.Sprintf(
"INSERT INTO users (name) VALUES %s",
strings.Join(values, ","),
)
Summary:
Files: 10
Lines: 181
Issues: 11
Luckily, linters like gosec can help you to prevent some vulnerabilities
Static code analysis: other tools
Check out all of them :)
Fuzzy testing
Fuzzy testing can help you to check if your validation is good enough
Vulnerabilities in Go?
Data
Code
Dependencies
Dependencies is one of the most favourite topics in the community
Dependencies: problem definition
Dependencies checklist
GOPROXY: pros
GOPROXY: cons
Security Onion Model
Data
Code
Container
Dependencies
Container
Builds
Multi-staging builds: Stage 0
# Stage 0. Build the binary
FROM artifactory/golang:1.12
# add a non-privileged user
RUN useradd -u 10001 myapp
RUN mkdir -p /go/src/github.com/rumyantseva/myapp
ADD . /go/src/github.com/rumyantseva/myapp
WORKDIR /go/src/github.com/rumyantseva/myapp
# Build the binary with go build
RUN CGO_ENABLED=0 go build \
-o bin/myapp github.com/rumyantseva/myapp/cmd/myapp
# Stage 1. Run the binary
…
Multi-staging builds: Stage 1
# Stage 1. Run the binary
FROM scratch
ENV PORT 8080
# certificates to interact with other services
COPY --from=0 /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# don't forget /etc/passwd from previous stage
COPY --from=0 /etc/passwd /etc/passwd
USER myapp
# and finally the binary
COPY --from=0 /go/src/github.com/rumyantseva/myapp/bin/myapp /myapp
EXPOSE $PORT
CMD ["myapp"]
Multi-staging builds: GOPROXY
# Initial stage: download modules
FROM artifactory/golang:1.12
ADD go.mod go.sum /m/
RUN cd /m && go mod download
While go.mod and go.sum are not changed, the stage will be cached
Base image for testing
FROM artifactory/golang:1.12
# Change here to update
ENV VERSION 1.12.3
ENV CHECKSUM c531688661b500d4c0c500fcf57f829388a4a9ba79697c2e134302aedef0cd46
# Make sure we have a fixed golangci-lint script with a chekcsum check
RUN echo "${CHECKSUM} golangci-lint-${VERSION}-linux-amd64.tar.gz" > CHECKSUM
# Download from Github the specified release and extract into the go/bin folder
RUN curl -L "https://github.com/golangci/golangci-lint/ releases/download/v${VERSION}/golangci-lint-${VERSION}-linux-amd64.tar.gz" \
-o golangci-lint-${VERSION}-linux-amd64.tar.gz \
&& shasum -a 256 -c CHECKSUM \
&& tar xvzf golangci-lint-${VERSION}-linux-amd64.tar.gz \
--strip-components=1 \
-C ./bin \
golangci-lint-${VERSION}-linux-amd64/golangci-lint
# Clean up
RUN rm -rf CHECKSUM "golangci-lint-${VERSION}-linux-amd64.tar.gz"
Run linters and tests
FROM artifactory/golang-linters
RUN mkdir -p /go/src/github.com/rumyantseva/myapp
ADD . /go/src/github.com/rumyantseva/myapp
WORKDIR /go/src/github.com/rumyantseva/myapp
# Run linters
RUN golangci-lint run \
--no-config --issues-exit-code=1 \
--deadline=10m --exclude-use-default=false \
./...
# Run tests
RUN go test -timeout=600s -v --race ./...
Operate containers
What we can learn?
hello@grahovac.pro
webdeva
rumyantseva
These slides and additional examples:
https://security.grahovac.pro
References and additional reading