15 ms·
Writing secure Go code
- hasnain99 2y agodoes it work
- gus_leonel 2y agoTIL about `gosec`.
- johnisgood 2y agoI use VSCodium when I am programming in Go, using the extension, because it has everything I need, and that includes gosec.
- goodlinks 2y agoDoes go have a bad security reputation? I get that anything can be insecure and its a constant battle as this article suggests, but i thought it was quite secure and stable generally (say on a par with .net or any other tool you may use to make a web app at least?)
- wbl 2y agoYou can write SQL injection in any language.
- jiehong 2y agoYou can use outdated dependencies in any language.
- valbaca 2y ago> i thought it was quite secure and stable generally It is, but security isn't a "given" anywhere. XSS, SQL Injection, Dependency etc can be done by any language, regardless of how "secure" it claims to be. The headings are all pretty general (versioning, tooling, scanning, testing) but the contents are Go-specific. It's a pretty good article IMO and could/should be replicated for other languages as well.
- jerf 2y agoNo. Ironically, a flip side of the complaints about how Go lacks power is that a lot of the "standard" security vulnerabilities actually become harder to write. The most obvious one is lacking the "eval" that a dynamic language has; more subtle ones include things like, there is no way to take a string and look up a type or a method in the runtime, so things like the Ruby YAML vuln are not assisted by the language level. To write something like that into Go, you'd have to actually write it in. Though you can, if you try hard enoough. But, as sibling comments point out, nothing stops you from writing an SQL injection. Command injections are inhibited by the command only taking the "array of strings" form of a command, with no "just pass me a string and we'll do shell things to it" provided by the language, but I've dispatched multiple questions about how to run commands correctly in Go by programmers who managed to find []string{"bash", "-c", "my command with user input from the web here"}, so the evidence suggests this is still plenty easy enough to write. Putting the wrong perms or no perms on your resources is as easy as anything else; no special support for internal security (compare with E lang and capabilities languages). And the file access is still based on file names rather than inodes, so file-based TOCTOUs are the default in Go (just like pretty much everywhere else) if you aren't careful. It comes with no special DOS protection or integrated WAF or anything else. You can still store passwords directly in databases, or as their MD5 sums. The default HTML templating system is fairly safe but you can still concatenate strings outside of the template system and ship them out over an HTTP connection in bad ways. Not every race condition is automatically a security vulnerability, but you can certainly write race conditions in Go that could be security vulnerabilities. I'd say Go largely lacks the footguns some other languages have, but it still provides you plenty of knives you can stab yourself with and it won't stop you. I've been running govulncheck against my repos for a while, and I have seen some real vulnerabilities go by that could have affected my code, but rather than "get arbitrary execution" they tend to be "didn't correctly escape output in some particular edge case", which in the right circumstances can still be serious, but is still at least less concerning than "gets arbitrary execution".
- Smaug123 2y ago> I'd say Go largely lacks the footguns some other languages have With the glaring exception of "I forgot to check the error code", which you need a linter (e.g. as provided by golangci-lint) for. It's critically important for security that you know whether the function you just called gave you a meaningful result! Most other languages either have sum types or exceptions.
- perryh2 2y ago> Does go have a bad security reputation? Depends on who's behind the keyboard.
- Cthulhu_ 2y agoReminds me of when SQL injection was the hot security problem, which was mainly caused by PHP, but not the language itself but reams and reams on low quality online tutorials trying to keep things simple by just concatenating GET parameters straight into an SQL query.
- tptacek 2y agoIt has essentially the same security properties of all the modern non-C-languages (ie, C, C++, ObjC), with the added bonus of largely being designed after the deserialization pandemic that especially hit Java, Python, and Ruby. ~All these modern languages are fine for security (though: be careful with serialization formats in anything but Go and Rust). Arguably, Rust and Go are the two "most secure" mainstream languages, but in reality I don't think it much matters and that you're likely to have approximately the same issues shipping in Python as in Rust (ie: logic and systems programming issues, not language-level issues). Be wary of anyone trying to claim that there are significant security differences between any of the "modern" or "high-level" languages. These threads inexorably trend towards language-warring.
- quietbritishjim 2y agoWhat is the "deserialisation pandemic"? It doesn't have obvious web search results, and I'm struggling to imagine what about deserialisation what be common between Java and Python (except that, in both cases, I'd surely just use protobuf if I wanted binary serialisation).
- kibwen 2y agoSee https://en.wikipedia.org/wiki/Log4Shell https://en.wikipedia.org/wiki/Log4Shell , but also historically the mess that is pickling/unpickling in Python (see the big scary warning at the top of https://docs.python.org/3/library/pickle.html#pickle-python-object-serialization https://docs.python.org/3/library/pickle.html#pickle-python-... ), and more broadly any dynamic language that exposes `eval` in any capacity.
- tptacek 2y agoFor many years, these were the most widespread serverside RCE vulnerabilities; Rails YAML might be the best-known, but there were a bunch of different variants in Java serialization, and a whole cottage subfield of vulnerability research deriving different sequences of objects/methods to bounce deserializations through. It was a huge problem, and my perception is that it sort of bled into SSRF (now the scariest vulnerability you're likely to have serverside) via XML deserialization.
- deleted 2y ago[deleted]
- pram 2y agoI've been maintaining a Go app for about 9 years now and I can just upgrade the Go version + mod for vulnerabilities (GitHub tells me about them automatically idk) and it works with no changes 99% of the time. I can't overstate how this makes maintaining it very stress-free. My JS apps on the other hand...
- valbaca 2y agoI shudder to think the amount of thousands of engineering hours are spent in my FAANG to keep our Java services just running as-is with updates. And now we're moving more to Typescript on Node...UGH.
- adhamsalama 2y agoI thought Java was robust. What's the hassle?
- coredog64 2y agoNot OP, but typically Spring and transitive dependencies. Some package that you don’t even use is pulled in and has a CVE. Or you upgrade major Spring versions and the API changes underneath you.
- koito17 2y agoHave people considered frameworks implementing JAX-RS instead? Or does the breakage happen specifically in extensions to Spring? The only inconvenience I have experienced upgrading a Quarkus backend is renaming javax.* package imports to jakarta.*. Hopefully the next major version requires just as little effort (if not less). I am sure there would have been a lot more work if the project used extensions like the Kubernetes client. But overall, I have had the best experience with tools like Maven (for Java) and Leiningen (for Clojure). It helps to avoid libraries that hack or access JDK internals (e.g. Lombok, or ancient libraries using sun.* internal packages for reflection)
- tapirl 2y agoPlease note, currently, there are no tools to detect the new footguns created by the new semantics of 3-clause "for;;" loops: https://github.com/golang/go/issues/66156 https://github.com/golang/go/issues/66156 > The second step is to keep the Go versions in our projects current. Even though we don’t use the latest and greatest language features, bumping the Go version gives us all security patches for discovered vulnerabilities. It is not always a good strategy to use the latest toolchain version. There are often some fresh bugs in it. From the security perspective, it is better to use the previous version, which is also still being maintained.
- arccy 2y agoyou're literally the only person who's making a mountain out of a molehill coming up with ever more convoluted code to "prove" the change was a bad thing.
- tapirl 2y agoIt indeed is. Please read https://go101.org/blog/2024-03-01-for-loop-semantic-changes-in-go-1.22.html https://go101.org/blog/2024-03-01-for-loop-semantic-changes-... and https://github.com/golang/go/issues/66156 https://github.com/golang/go/issues/66156
- candiddevmike 2y ago.0 is for local development, .1+ is for production
- rollulus 2y agoAs the article also mentions: instead of checking if your program has a dependency on something that contains vulnerabilities, govulncheck checks if vulnerable code is actually reached. I find that so awesome. (And I know, someone is going to point out that hipster language foo does this too and better — it’s not the norm).
- lynx23 2y ago> hipster language Funny, I always considered Go a hipster language for Google fanboys.
- paulddraper 2y agoYou either die a hipster or live long enough to become mainstream.
- Cthulhu_ 2y agoGo is a retro nostalgia language, taking programming languages back to basics, removing syntax highlighting and advanced concepts like exceptions and function shorthands because that's what it was like in the 70's.
- euroderf 2y agoI'll give up my Go when you pry this PDP-11 emulation from my cold, dead hand.
- rob74 2y agoPray tell, what is it about Go that stops editors from using syntax highlighting when displaying Go code? Seriously, IMHO Go is less "retro nostalgia" and more trying to stick to proven concepts (e.g. there was no test driven development in the 70s, and Go has testing/documentation/examples built into the language) while leaving out things like exceptions and inheritance that, while widespread, have significant disadvantages.
- 2y ago
- dakiol 2y agoGo is nice, but the recent trend of using generics for many stuff is making harder and harder to keep Go code readable imho. See an example here https://eli.thegreenplace.net/2024/ranging-over-functions-in-go-123/ https://eli.thegreenplace.net/2024/ranging-over-functions-in... I'm not saying it's hard to read, but it's harder than previous Go code that used little or no generics at all.
- mervz 2y agoMeanwhile, error handling still can't get any sort of syntactic sugar
- divan 2y agoAs with real sugar, we humans don’t have sensors that would tell us when there’s "too much sugar".
- randomdata 2y agoThat's because nobody has yet solved the side effect problem of the sugar. All the proposals that have ever been given have ultimately boiled down to essentially `return err`, which, while suitable for meme comments on an internet forum, cannot be used in a real production application for many obvious (and some not immediately obvious) reasons. At least under the direction of rsc (the new leadership is still settling into the role so that is less clear), the will to add such sugar was there if a good solution was found. But the solution has yet to be found.
- jimbokun 2y agoI don't know what the syntax should look like. But the most common pattern is a sequence of calls to functions that return an optional error plus the happy path value, followed by a short circuiting check of the error, followed by a call to another function with the happy path value as an argument. It's very common to have a chain of these kinds of calls making up the body of a function. It seems like "return err" is very useful for this pattern, if I understand you correctly. A function returning the error from the first call it makes that fails, or the happy path value if all the calls succeed. Seems like it should be possible to bake that pattern into the language, but its tricky doing it a way that doesn't obfuscate the underlying semantics, which is very important to many Go developers.
- wepple 2y agoDon’t forget about capslock: https://github.com/google/capslock https://github.com/google/capslock Assess your 3P modules for dangerous capabilities
- rollulus 2y agoNice, thanks a lot for that!
- K0nserv 2y agoSomewhat related, I learned a surprising fact recently: Go is not actually memory safe. In particular because atomicity is only guaranteed for word size values, double word values(interface pointers, slices) can introduce memory unsafety in the presence of concurrency[0]. It's one of those things that feels obvious when you see it. 0: https://blog.stalkr.net/2015/04/golang-data-races-to-break-memory-safety.html https://blog.stalkr.net/2015/04/golang-data-races-to-break-m...
- tptacek 2y agoIt's easy to demonstrate contrived abuses of Go concurrency that break memory safety, but despite the enormous popularity of the language, actual shipping vulnerabilities --- mistakes in concurrency, not deliberately-engineered pathological cases, that yield attacker-controlled control over memory --- are basically nonexistent (I can't think of a single one; there must be one somewhere!). Basically this is about as credible an argument as claiming that Rust isn't memory safe because its libraries have so much `unsafe` code. And that claim: not super credible. Basically, the takeaway in both cases is that it's not safe to allow an attacker to write code for you in the language. But everybody assumes that's the case anyways, because it's the case with virtually every other language (with one very notable, fraught, and well-understood exception), too.
- shitter 2y agoWhat is that exception?
- ViewTrick1002 2y ago
- isomorphismism 2y ago[flagged]
- jjcm 2y agoGreat tips in here - I was not aware of `go vet` nor `go test -race`. FWIW, while go is not memory safe, I do find that it's much easier to be safe in go than it is in other languages. Its verboseness lends to a very clear understanding of what's happening in any given function. I absolutely hated this at the start, but now ~3 years into maintaining a go codebase, I find it quite nice both for debugging as well as editing old code. I know exactly what each function does, and what the structure of data is in any given context. Another interesting side effect is that AI tools seem to work amazingly well with golang, given how context is often local to the function.
- tptacek 2y agoGo is memory-safe. It's not the definition of "memory-safe language" that it's impossible to write memory-unsafe code, only that ordinary code is memory-safe by default.
- thinkharderdev 2y ago> ordinary code is memory-safe by default What does that mean? What constitutes "ordinary"? I'm not sure there is any official definition of memory safety, but I would consider it to mean that aside from code that is explicitly marked as unsafe it is impossible to write code that has undefined behavior.
- tptacek 2y agoAn example of extraordinary code would be code that interfaces with and/or pulls in non-memory-safe legacy C code. Another example would be code specifically contrived to highlight a soundness problem in the language. I used the term "extraordinary" to avoid exactly this kind of bickering over corner cases that aren't relevant to day-to-day software development (or at least, not in ways that aren't immediately evident when they come up.)
- thinkharderdev 2y ago> An example of extraordinary code would be code that interfaces with and/or pulls in non-memory-safe legacy C code. That's my point though. Of course calling non-memory safe native code over FFI can lead to memory-safety problems in any language. Likewise using the "unsafe" subset that basically every language has. But none of that is required in Go. It is only required that you mutate shared state from different threads, which is something that I would imagine happens in a lot of Go code codebases since it is an extremely easy mistake to make. To be clear I think: 1. Go is mostly a memory safe language because it does in fact prevent the most common memory safety issues in C/C++ (UAF, buffer overflows, etc) 2. It is LESS memory safe than other modern memory-sage languages (Rust, Java, C#, Python, etc....) 3. The memory safety issues in Go are very difficult to exploit in code that is not specifically crafted to surface them
- tbiehn 2y agoSemgrep is another great option to get value out of static analysis checks against both the language and a few common frameworks. It remains a popular choice for security folks writing static detection rules (and contributing them to the commons). You can check the open rules here; https://github.com/semgrep/semgrep-rules/tree/develop/go https://github.com/semgrep/semgrep-rules/tree/develop/go
- Xaki8271 2y agoWow!