229 points by ngaut 5 days ago
Several of our clients have Go codebases. My general experience as a security auditor of Go projects has been that it is mostly akin to security auditing of Python projects (I'd say it's like Java auditing, but I'm very concerned/excited about deserialization bugs in Java programs and am not so optimistic about finding them in Go programs).
Which is to say, the low hanging fruit won't be exploitable type safety problems, but rather application logic issues: SQL injection (Go's database integration is still the wild west), failure to properly authorize RPCs or HTTP endpoints, SSRF, and stuff like that. I'm probably not going to use property testing to find an SSRF, or to spot a static nonce, or something like that (somebody feel free to put me my place over that! maybe I should use more property testing!)
It's interesting that the strategies described in this post lean so heavily on theoretical program correctness; as I read it, it felt super useful to me as a Go developer, and less directly applicable to my assessment work.
Relatedly, this post was circulating on Twitter yesterday, and it is great: a race condition in Go code exploitable for RCE:
I probably had your attention with that summary! Race conditions in Go code could be a broadly exploitable bug class! Except: not so much, no: the conditions making that bug exploitable are both contrived and outlandish enough that no security reviewer, even one unfamiliar with Go, would have been comfortable with that design.
> It's interesting that the strategies described in this post lean so heavily on theoretical program correctness; as I read it, it felt super useful to me as a Go developer, and less directly applicable to my assessment work.
It's an artifact of the type of clients we tend to get: microservices, blockchain software, and Kubernetes (itself an amalgamation of many loosely connected components). These tend to be easier to test (less code, less state) and require higher assurance so we hit correctness properties a lot more commonly than, say, a monolithic web application.
> (somebody feel free to put me my place over that! maybe I should use more property testing!)
There is a part 2 to this blog post that you will enjoy! =D
> SQL injection (Go's database integration is still the wild west)
Could you elaborate on this? I thought you were safe if you used the '?' parameter replacement.
Oh, you are, to the extent that you are when you directly use parameterized queries anywhere (ie, there are corner cases). But unlike Java-world and Python-world and Ruby-world, there aren't really idiomatic mainstream heavyweight database interfaces in Go-world, so if your application is dynamically composing its own SQL queries, which many serious applications end up doing, you can end up with concatenated SQL and the attendant bugs.
I'm not going to go into a fresh Django project optimistic about my chances of finding SQLI, but a database-heavy Go program, that's one of the first things I'm going to take a whack at.
Just putting it out there to spread the word: sqlx and sqlz Go libraries are very good. They take a lot of pain out of mapping Go structs to resultsets and also prevent SQL Injection. sqlx seems to be quite popular but sqlz could do with some love.
Do you think Go might have better DB libraries if generics ever land? Or what else might be holding it back?
I don't know. I wrote a code-generating ORM for my own projects, which is what I use. But lack of generics isn't, for example, what makes Go projects hew so closely to net/http and net/http compatible interfaces, rather than the more elaborate interfaces people write in Python and Ruby; there's also just a "less is more" ethos in Go-world.
You don't think gorm is mainstream? It isn't the only choice, but is used pretty extensively.
We use it on all our projects though it really needs improved/additional m:n handling.
I'm familiar with gorm, but haven't seen it at any of our clients.
Fair, perhaps we can fix that with an audit soonish :)
You should be, but because a lot sql in Go is still just strings it makes it easy for someone to bypass because of laziness/sloppiness/distracted/whatever when composing queries. There is also the issue that if the Go code needs to run on Postgres it has to use '$' params instead of '?'. So again, back to string concatenation to support both or use some library like squirrel.
Is it common to write apps that target multiple databases with SQL as the abstraction point? SQL behaves so differently across vendors (with respect to performance, extensions, etc) that I would be more inclined to write distinct queries for each vendor rather than try to find strings optimal for every vendor.
It used to be much more common when we shipped software to enterprises that required particular databases.
It’s much less common in SaaS environments.
"SQL Injection (Go's database integration is still the wild west)"
Is something like this prone to SQL injection ? I am very new to Go and still learning how to interact with databases (I am not a big fan of ORMs).
stmt = "UPDATE users SET last_login = $1 WHERE id = $2"
_, err = services.DB.ExecContext(ctx, stmt, time.Now(), id)
That is the correct approach.
That would only be vulnerable if the DB client is replacing your parameterized query with string concatenation under the hood.
There is another implementation of failpoints for Golang. Which is used by TiDB. See more details: https://github.com/pingcap/failpoint
If Go has memory management, what does fuzzing Go applications yield in terms of security defects other than D.o.S. crashes?