ksgi
/
tutorial6.xml
244 строки · 9.4 Кб
1<article data-sblg-article="1" data-sblg-tags="tutorial" itemscope="itemscope" itemtype="http://schema.org/BlogPosting">2<header>3<h2 itemprop="name">4Best practises for <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> security5</h2>6<address itemprop="author"><a href="https://github.com/kristapsdz">Kristaps Dzonsons</a></address>7<time itemprop="datePublished" datetime="2018-04-06">6 April, 2018</time>8</header>9<p>10<aside itemprop="about">11Let's set the record straight for securing <span class="nm">kcgi</span> CGI and FastCGI applications with <a12href="https://man.openbsd.org/pledge.2">pledge(2)</a>.13This is focussed on secure <a href="https://www.openbsd.org">OpenBSD</a> deployments.14</aside>15</p>16<h3>17Theory
18</h3>19<p>20Internally, <span class="nm">kcgi</span> makes considerable use of available security tools.21But it's also designed to be invoked in a secure environment.
22We'll start with <a href="https://man.openbsd.org/pledge.2">pledge(2)</a>, which has been around on <a23href="https://www.openbsd.org">OpenBSD</a> since version 5.9.24If you're reading this tutorial, you're probably on <a href="https://www.openbsd.org">OpenBSD</a>, and you probably have25knowledge of <a href="https://man.openbsd.org/pledge.2">pledge(2)</a>.26</p>27<p>28How to begin?
29Read <a href="kcgi.3.html">kcgi(3)</a>.30It includes canonical information on which <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> promises you'll need for31each function in the library.
32This is just a tutorial—the manpage is canonical and overrides what you may read here.33</p>34<p>35Next, assess the promises that your application needs.
36From <a href="kcgi.3.html">kcgi(3)</a>, it's easy to see which promises we'll need to start.37You'll need to augment this list with whichever tools you're also using.
38The general push is to start with the broadest set of required promises, then restrict as quickly as possible.
39Sometimes this can be done in a single <a href="https://man.openbsd.org/pledge.2">pledge(2)</a>, but other times it takes a40few.
41</p>42<figure>43<img class="tall" src="tutorial6.svg" alt="" />44</figure>45<h3>46Source Code: CGI
47</h3>48<p>49Let's start with a trivial CGI application.
50This parses the HTTP context with <a href="khttp_parse.3.html">khttp_parse(3)</a>, then emits output using the writing51functions.
52Most applications will perform some sort of work based upon the context, such as with form input, but it's irrelevant for the
53purposes of explanation.
54</p>55<p>56I avoid error checking for brevity, but <strong>each function needs to be checked for return values</strong>.57This goes for all examples, all the time!
58</p>59<figure class="sample">60<pre class="prettyprint linenums">#include <sys/types.h> /* size_t, ssize_t */61#include <stdarg.h> /* va_list */62#include <stddef.h> /* NULL */63#include <stdint.h> /* int64_t */64#include <kcgi.h>65
66int
67main(void) {
68struct kreq r;
69const char *const pages[1] = { "index" };
70
71if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)72return 0;
73khttp_head(&r, kresps[KRESP_STATUS],74"%s", khttps[KHTTP_200]);
75khttp_head(&r, kresps[KRESP_CONTENT_TYPE],76"%s", kmimetypes[KMIME_TEXT_PLAIN]);
77khttp_body(&r);78khttp_puts(&r, "Hello, world!\n");79khttp_free(&r);80return 0;
81}</pre>82</figure>83<p>84Obviously, this does very little.
85And that's fine, because <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> doesn't really care about how complex your86application is—just which system resources are used.87Our main focus is going to be <a href="khttp_parse.3.html">khttp_parse(3)</a>, which requires the system resources to create88its parsing contexts and safely get our data extracted and processed.
89After that, all we're doing is constructing a response.
90</p>91<p>92To use this security tool, all we need is to include <code><unistd.h></code> and to understand the <a93href="https://man.openbsd.org/pledge.2">pledge(2)</a> system call.94We'll start with the most significant protection: by constraining our application after the context has been parsed.
95</p>96<figure class="sample">97<pre class="prettyprint linenums">if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)98return 0;
99if (pledge("stdio", NULL) == -1)
100return 0;</pre>101</figure>102<p>103This <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> promise restricts the process to simply using available I/O104channels.
105The only <span class="nm">kcgi</span> functions requiring more than <code>"stdio"</code> are the file-based template functions106described in <a href="khttp_template.3.html">khttp_template(3)</a> and <a href="khttp_template.3.html">khttp_templatex(3)</a>,107which also require <code>"rpath"</code>.108</p>109<p>110We can further secure our systems by pushing <a href="https://man.openbsd.org/pledge.2">pledge(2)</a> <em>before</em> the call111to <a href="khttp_parse.3.html">khttp_parse(3)</a>.112The only promises <a href="khttp_parse.3.html">khttp_parse(3)</a> requires are to <a113href="https://man.openbsd.org/fork.2">fork(2)</a> its internal handlers.114</p>115<figure class="sample">116<pre class="prettyprint linenums">if (pledge("stdio proc", NULL) == -1)117return 0;
118if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)119return 0;
120if (pledge("stdio", NULL) == -1)
121return 0;</pre>122</figure>123<p>124Or if one is using <a href="khttp_template.3.html">khttp_template(3)</a> to marshall a response:125</p>126<figure class="sample">127<pre class="prettyprint linenums">if (pledge("stdio rpath proc", NULL) == -1)128return 0;
129if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)130return 0;
131if (pledge("stdio rpath", NULL) == -1)
132return 0;</pre>133</figure>134<p>135That's it!
136Obviously, you'll need to expand the set of promises proportionate to your application's needs.
137Let's put it all together:
138</p>139<figure class="sample">140<pre class="prettyprint linenums">#include <sys/types.h> /* size_t, ssize_t */141#include <stdarg.h> /* va_list */142#include <stddef.h> /* NULL */143#include <stdint.h> /* int64_t */144#include <unistd.h> /* pledge */145#include <kcgi.h>146
147int
148main(void) {
149struct kreq r;
150const char *const pages[1] = { "index" };
151
152if (pledge("stdio proc", NULL) == -1)
153return 0;
154if (khttp_parse(&r, NULL, 0, pages, 1, 0) != KCGI_OK)155return 0;
156if (pledge("stdio", NULL) == -1)
157return 0;
158khttp_head(&r, kresps[KRESP_STATUS],159"%s", khttps[KHTTP_200]);
160khttp_head(&r, kresps[KRESP_CONTENT_TYPE],161"%s", kmimetypes[KMIME_TEXT_PLAIN]);
162khttp_body(&r);163khttp_puts(&r, "Hello, world!\n");164khttp_free(&r);165return 0;
166}</pre>167</figure>168<p>169One last note: portability.
170If you're going to be writing CGI scripts that must be portable across architectures, consider using
171<a href="https://github.com/kristapsdz/oconfigure">oconfigure</a> for a selection of portable OpenBSD functions and feature tests.172</p>173<h3>174Source Code: FastCGI
175</h3>176<p>177This follows the logic of the CGI example, but we need to have extra promises to account for the increased complexity of FastCGI processing.
178First, start with our simple example without any header inclusions—all of which are the same as in the CGI example.179</p>180<p>181Again, <strong>each function needs to be checked for return values</strong>.182This would otherwise clutter up the examples, but production systems <strong>must</strong> error check.183</p>184<figure class="sample">185<pre class="prettyprint linenums">int186main(void) {
187struct kreq r;
188const char *const pages[1] = { "index" };
189struct kfcgi *fcgi;
190enum kcgi_err er;
191
192if (khttp_fcgi_init(&fcgi, NULL, 0, pages, 1, 0) != KCGI_OK)193return 0;
194for (;;) {
195if (khttp_fcgi_parse(fcgi, &req) != KCGI_OK)196break;
197khttp_head(&r, kresps[KRESP_STATUS],198"%s", khttps[KHTTP_200]);
199khttp_head(&r, kresps[KRESP_CONTENT_TYPE],200"%s", kmimetypes[KMIME_TEXT_PLAIN]);
201khttp_body(&r);202khttp_puts(&r, "Hello, world!\n");203khttp_free(&r);204}
205return 0;
206}</pre>207</figure>208<p>209The FastCGI-specific functions we need to manage are
210<a href="khttp_fcgi_init.3.html">khttp_fcgi_init(3)</a> and211<a href="khttp_fcgi_parse.3.html">khttp_fcgi_parse(3)</a>.212The promises required are all noted in <a href="kcgi.3.html">kcgi(3)</a>.213The rest follow from the CGI example.
214</p>215<figure class="sample">216<pre class="prettyprint linenums">int217main(void) {
218struct kreq r;
219const char *const pages[1] = { "index" };
220struct kfcgi *fcgi;
221enum kcgi_err er;
222
223if (pledge("unix sendfd recvfd proc stdio", NULL) == -1)
224return 0;
225if (khttp_fcgi_init(&fcgi, NULL, 0, pages, 1, 0) != KCGI_OK)226return 0;
227if (pledge("stdio recvfd", NULL) == -1)
228return 0;
229for (;;) {
230if (khttp_fcgi_parse(fcgi, &req) != KCGI_OK)231break;
232khttp_head(&r, kresps[KRESP_STATUS],233"%s", khttps[KHTTP_200]);
234khttp_head(&r, kresps[KRESP_CONTENT_TYPE],235"%s", kmimetypes[KMIME_TEXT_PLAIN]);
236khttp_body(&r);237khttp_puts(&r, "Hello, world!\n");238khttp_free(&r);239}
240khttp_fcgi_free(fcgi);
241return 0;
242}</pre>243</figure>244</article>245