% vi main.c
% vi main.c
Anybody can write crappy, bug-ridden and insecure code. In any language. Good news: with C, it's even easier! So familiarise yourself with common (and common-sense) pitfalls noted in the SEI CERT C coding standard and comp.lang.c FAQ.
To encourage sharing, consider style(9) as a guide
for the style of your C code.
It makes a significant difference when bringing on victims
If you don't want to parse content by hand—and you really, really don't—let a library do it for you. Install and use kcgi or fcgi from packages.
Caveat emptor on FastCGI. Be careful! CGI scripts run by slowcgi(8) already have their privileges dropped and file-system constrained. With FastCGI, you'll need to use a framework that does so for you!
OpenBSD has pledge(2).
Embrace the pledge.
Your application will be under assault every moment it's running. And it will have bugs: limit the damage caused by inevitable mistakes by constraining (sandboxing) the environment available to your application. Oh, right: there's also chroot(2), dropping privileges, etc…
For more on these security tools, see Secure CGI.
Spend time carefully reading through the SQLite documentation. For a simpler API (and some significant security features), consider using ksql(3). Most folks knee-jerk into needing the biggest Oracle or NoSQL database they can find. But for most applications, it's perfectly alright to start with SQLite and vanilla SQL, then slowly expand as necessary. The usual path is from SQLite to PostgreSQL.
Statically scan your binary with LLVM (from packages). There's also frama-c and splint, both also in packages.
These static analysis tools can help catch source-level bugs. An alternative compiler (LLVM) can also prevent you from relying too much on gcc(1)'s extensions, which reduces portability.
If your project is open source, you can register it with Coverity, too.
Run your application under valgrind (from packages) and make sure your malloc.conf(5) flags are unforgiving.
Memory errors are often the subtlest (next to race conditions, maybe). Use these tools as much as possible to catch any run-time issues that can't be ferreted with static analysis tools.
Parsing data? Push your parsers into libraries and use AFL (from packages) to put them through inputs you haven't tested.
The thought that
this input could never happen is your adversary's favourite.
It will happen.
Make sure any parsing sequences are being run through as many code-paths as
possible with this tool.
Convinced? See it in action.