You've deployed BCHS. Your editor is open. Where do you begin?
Don't just write C. Write portable and secure 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.
Be familiar with CGI and FastCGI.
Install and use kcgi* or fcgi from packages. (See also kwebapp*.)
If you don't want to parse content by hand—and you really, really don't—let a library do it for you. (Want 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!)
Get to know your security tools. 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.
Databases: simplicity rules.
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 happenis 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.
Don't forget: BSD is a community of professionals. Go to conferences (EuroBSDCon, AsiaBSDCon, BSDCan, etc.). Subscribe to the mailing lists. Donate. And lastly but most important of all: use the man pages! (SQLite doesn't have its own bundled manpages: you'll need to use sqlite2mdoc*.)
Convinced? See it in action.
*Disclaimer: I (Kristaps) wrote this.