kcgi: Minimale CGI- en FastCGI-bibliotheek voor C/C++

Voorbeeld: Hello World

Het volgende voorbeeld geeft "Hello, World!" terug als een HTTP-respons op een CGI-verzoek.

#include <sys/types.h> /* size_t, ssize_t */
#include <stdarg.h>    /* va_list */
#include <stddef.h>    /* NULL */
#include <stdint.h>    /* int64_t */
#include <kcgi.h>

int main(void) {
    struct kreq r;
    const char *page = "index";

    /*
     * Analyseer de HTTP-omgeving.
     * We herkennen slechts één pagina, "index", wat ook
     * de standaardpagina is als er geen pagina wordt opgegeven.
     * (Er vindt geen validatie van invoervelden plaats.)
     */
    if (khttp_parse(&r, NULL, 0, &page, 1, 0) != KCGI_OK)
        return 1;

    /*
     * Normaal gesproken zou ik hier schakelen op de methode (OPTIONS, etc.,
     * gedefinieerd in de methode-variabele) en vervolgens op de
     * gevraagde pagina (page-variabele).
     * Voor de beknoptheid sturen we direct een respons: HTTP 200.
     */
    khttp_head(&r, kresps[KRESP_STATUS],
               "%s", khttps[KHTTP_200]);

    /*
     * Stel het content-type eenzijdig in als text/plain.
     * Normaal gesproken zou dit worden ingesteld via r.mime.
     */
    khttp_head(&r, kresps[KRESP_CONTENT_TYPE],
               "%s", kmimetypes[KMIME_TEXT_PLAIN]);

    /* Geen HTTP-headers meer: start het lichaam van het HTTP-document. */
    khttp_body(&r);

    /*
     * Hieronder kan elke gewenste inhoud worden geplaatst: JSON, HTML, etc.
     * Meestal zouden we schakelen op basis van ons MIME-type.
     * In dit geval plaatsen we enkel de letterlijke tekst...
     */
    khttp_puts(&r, "Hello, world!");

    /* Flush het document en geef resources vrij. */
    khttp_free(&r);

    return 0;
}

Voor uitgebreidere voorbeelden kunt u kijken naar sample.c (C en CGI), samplepp.cc (C++ en CGI), sample-fcgi.c (C en FastCGI), of naar de sectie Documentatie gaan.

Functionaliteiten

kcgi ondersteunt diverse functies, waaronder:

  • Automatische compressie.
  • Afhandeling van alle HTTP-inputoperaties (query strings, cookies, paginalichamen, multipart) inclusief validatie.
  • Authenticatie.
  • Configureerbare output-caching.
  • Debugging van verzoeken.
  • Geformatteerde schrijvers (JSON, XML, etc.).

Het belangrijkste onderscheidende kenmerk is het gebruik van sandboxing en processcheiding voor het afhandelen van onbetrouwbare input.

Actuele Versie

Versie 1.0.1 (17-11-2025): Hot-fix voor versie 1.0.0 met correcte naamgeving van Mac OS X shared libraries, aangezien deze een andere conventie voor versiesuffixen gebruiken dan andere Unix-achtige systemen.

Installatie

Controleer eerst of kcgi al beschikbaar is als pakket voor uw systeem, bijvoorbeeld via:

  • pkg_add kcgi (OpenBSD)
  • pkg install kcgi (FreeBSD)
  • brew install kcgi (Homebrew)
  • pacman -S kcgi (Arch Linux)

Indien dit het geval is, installeer het pakket via uw systeembeheerder. Zo niet, dan is kcgi gebouwd en uitgevoerd op de volgende systemen voor i386, amd64, powerpc, arm64 en sparc64:

  • GNU/Linux (musl en glibc)
  • OpenBSD
  • NetBSD
  • FreeBSD
  • Solaris
  • omniOS
  • Mac OS X (alleen Mojave en nieuwer)

U kunt kcgi.tgz downloaden en het archief verifiëren met kcgi.tgz.sha512. Voor de allernieuwste code tussen releases door kunt u terecht op GitHub. Instructies voor het bouwen en installeren zijn te vinden op de repository-pagina.

Afhankelijkheden:

  • De enige harde afhankelijkheid tijdens het bouwen is BSD make (bmake op Linux) en zlib.
  • Voor het uitvoeren van regressietests is libcurl vereist.

Implementatie

Om kcgi te gebruiken, heeft u een webserver nodig die CGI of FastCGI kan uitvoeren. Apache, nginx en OpenBSD's httpd(8) zijn uitgebreid gebruikt, waarbij de laatste twee via FastCGI en de slowcgi-wrapper werken.

Compileren

Gebruik de pakketconfiguratie om kcgi-applicaties te compileren. Het linken gebeurt op een vergelijkbare manier:

% cc `pkg-config --cflags kcgi` -c yourprog.c
% cc yourprog.o `pkg-config --libs kcgi`

Webservers die goed zijn geconfigureerd (zoals de standaard OpenBSD-server) worden standaard binnen een chroot(2) geplaatst. In dat geval moet u uw binary statisch linken (of alle shared libraries naar de shadow root kopiëren):

% cc -static yourprog.o `pkg-config --static --libs kcgi`

FastCGI-applicaties kunnen direct door de webserver worden gestart (populair bij Apache) of extern via een socket en kfcgi(8) (de norm voor OpenBSD's httpd(8) en aanbevolen vanwege de beveiligingsmaatregelen van de wrapper).

Documentatie

De manpages van kcgi, beginnend met kcgi(3), zijn de canonieke bron van documentatie.

Lijst van manpages

  • kcgi.3
  • kcgibufprintf.3
  • kcgibufputc.3
  • kcgibufputs.3
  • kcgibufwrite.3
  • kcgi_strerror.3
  • kcgiwriterdisable.3
  • kcgiwriterfree.3
  • kcgiwriterget.3
  • kcgiwriterputc.3
  • kcgiwriterputs.3
  • kcgiwriterwrite.3
  • kcgihtml.3
  • kcgijson.3
  • kcgiregress.3
  • kcgixml.3
  • kfcgi.8
  • khtml_attr.3
  • khtml_close.3
  • khtml_closeelem.3
  • khtml_closeto.3
  • khtml_double.3
  • khtml_elem.3
  • khtml_elemat.3
  • khtml_entity.3
  • khtml_int.3
  • khtml_ncr.3
  • khtml_open.3
  • khtml_printf.3
  • khtml_putc.3
  • khtml_puts.3
  • khtml_write.3
  • khttp_body.3
  • khttp_datetime2epoch.3
  • khttp_epoch2datetime.3
  • khttp_epoch2str.3
  • khttp_epoch2tms.3
  • khttpfcgifree.3
  • khttpfcgigetfd.3
  • khttpfcgiinit.3
  • khttpfcgiparse.3
  • khttpfcgitest.3
  • khttp_free.3
  • khttp_head.3
  • khttp_parse.3
  • khttp_printf.3
  • khttp_putc.3
  • khttp_puts.3
  • khttp_template.3
  • khttp_templatex.3
  • khttp_urlabs.3
  • khttp_urldecode.3
  • khttp_urlencode.3
  • khttp_urlpart.3
  • khttp_write.3
  • khttpbasic_validate.3
  • khttpdigest_validate.3
  • kjsonarrayclose.3
  • kjsonarrayopen.3
  • kjson_close.3
  • kjsonobjclose.3
  • kjsonobjopen.3
  • kjson_open.3
  • kjson_putbool.3
  • kjson_putdouble.3
  • kjson_putint.3
  • kjson_putnull.3
  • kjson_putstring.3
  • kjsonstringclose.3
  • kjsonstringopen.3
  • kjsonstringwrite.3
  • kmalloc.3
  • kutil_invalidate.3
  • kutil_log.3
  • kutil_openlog.3
  • kvalid_string.3
  • kxml_close.3
  • kxml_open.3
  • kxml_pop.3
  • kxml_popall.3
  • kxml_prologue.3
  • kxml_push.3
  • kxml_pushnull.3
  • kxml_putc.3
  • kxml_puts.3
  • kxml_write.3

Inleidend materiaal

Naast de manpages zijn er tutorials beschikbaar:

  • Aan de slag met CGI in C: Beschrijft een typisch CGI-voorbeeld waarbij HTML-formulierinput (string en 64-bit integer) wordt verwerkt en getoond op een HTML-pagina.
  • CGI Cookies beheren in C: Uitleg over het instellen, herkennen en opslaan van cookies via de HTTP-headerfunctionaliteit.
  • FastCGI Implementaties: Hoe u een eenvoudige FastCGI-applicatie implementeert met kfcgi(8) voor een hogere doorvoersnelheid.
  • Aangepaste Validatie: Voorbeelden over hoe u de validatiefunctie kunt overschrijven voor types die verder gaan dan doubles, integers of strings.
  • Het gebruik van Pagina's: Een overzicht van de basispad-afhandeling die door kcgi wordt geboden.
  • CGI voor C++ applicaties: Instructies voor het compileren van een C++ applicatie die de kcgi-bibliotheek gebruikt.
  • Best practices voor pledge(2) beveiliging: Gericht op veilige OpenBSD-implementaties.
  • CORS en kcgi: Bespreekt de enumeraties en functies in kcgi(3) om CORS-verzoeken (Cross-Origin Resource Sharing) af te handelen.

Conferentiesessies en publicaties

kcgi is referentie geweest in de volgende sessies door Kristaps Dzonsons:

  • AsiaBSDCon, Tokyo (maart 2018): Role-based Access Control in BCHS Web Applications.
  • AsiaBSDCon, Tokyo (maart 2017): Secure BSD Web Applications in C: Practical Strategies.
  • AsiaBSDCon, Tokyo (maart 2016): Secure BSD Web Application Development in C.
  • AsiaBSDCon, Tokyo (maart 2015): kcgi: securing CGI applications in C.

Daarnaast is er de publicatie:

  • Maart 2016: FastCGI Extensions for Management Control.

Implementatiedetails

Het grootste deel van de CGI-afhandeling van kcgi bevindt zich in khttpparse(3), die het HTTP-verzoek volledig analyseert. Ontwikkelaars moeten deze functie aanroepen vóór alle andere functies. Voor FastCGI is deze functionaliteit gesplitst tussen khttpfcgiinit(3) (initialisatie van context) en khttpfcgiparse(3) (ontvangst van nieuwe geanalyseerde verzoeken). In beide gevallen moeten verzoeken worden vrijgegeven via khttpfree(3).

Alle functies isoleren de analyse en validatie van onbetrouwbare netwerkgegevens binnen een gesandboxte child-proces. Sandboxes beperken de omgeving van een proces, waardoor exploiteerbare fouten in het analyseproces (of validatie met externe bibliotheken) de systeemomgeving niet kunnen beïnvloeden. Deze geanalyseerde gegevens worden via een socket teruggestuurd naar het parent-proces.

Deze methode van sandboxing volgt het voorbeeld van OpenSSH en vereist specifieke afhandeling per besturingssysteem:

  • Linux: Gebruikt seccomp(2) (vereist een seccomp-enabled kernel en ondersteunde architectuur), aangevuld met setrlimit(2).
  • OpenBSD: Gebruikt pledge(2) (werkt op OpenBSD >5.8).
  • Apple OSX: Gebruikt sandbox_init(3) voor pure computation (vanaf Mac OS X Leopard), aangevuld met setrlimit(2).
  • FreeBSD: Gebruikt de capabilities-faciliteit via capsicum(4) (vanaf FreeBSD 10), aangevuld met setrlimit(2).

Omdat validatie binnen de sandbox plaatsvindt, mogen validatieroutines de omgeving niet benaderen (bijv. geen bestanden openen of netwerkverbindingen maken), aangezien het child-proces anders abrupt door de sandbox-faciliteit kan worden beëindigd.

De connection delegator werkt vergelijkbaar, maar heeft andere sandbox-regels omdat deze een open socket-verbinding moet beheren en op nieuwe verzoeken moet reageren.

Testen

kcgi wordt geleverd met een volledig geautomatiseerd testframework dat kan worden uitgevoerd met make regress. Voor het testen van eigen applicaties kan de kcgiregress(3) bibliotheek worden gebruikt.

Dit framework fungeert als een mini-webserver die luistert op een lokale poort, een HTTP-document vertaalt naar een minimaal CGI-verzoek en dit doorstuurt naar een kcgi CGI-client. Voor interne tests worden verzoeken geconstrueerd met libcurl. De lokale poort is vastgesteld; indien u de regressiesuite uitvoert, moet u mogelijk de toegangspoort aanpassen.

Het testframework wordt gebruikt door GitHub Actions om te garanderen dat alle commits de regressietests passeren op alle ondersteunde systemen (inclusief compiler-sanitisatie).

Er bestaat ook een testframework voor gebruik met American Fuzzy Lop (AFL). Hiervoor moet het make afl target worden gecompileerd (bijv. make clean, dan make afl CC=afl-gcc). Vervolgens kan de afl-fuzz tool worden gedraaid op de afl-multipart, afl-plain, en afl-urlencoded binaries met de meegeleverde testcases en dictionaries.

Prestaties

Beveiliging heeft een prijs, hoewel deze niet extreem hoog is. Door het ontwerp ontstaat er overhead op drie manieren:

  1. Het spawnen van een child-proces om onbetrouwbare netwerkgegevens te verwerken.
  2. Het activeren van het sandbox-framework.
  3. Het terugsturen van de geanalyseerde paren naar de parent-context.

Bij het uitvoeren van CGI-scripts zijn de prestaties van kcgi gebonden aan het vermogen van het besturingssysteem om processen te spawnen en te oogsten (reap). Voor FastCGI ligt de bottleneck bij de overdracht van gegevens.