haserl(1) | General Commands Manual | haserl(1) |
NAME¶
haserl - A cgi scripting program for embedded environmentsSYNOPSIS¶
#!/usr/bin/haserl [--shell=pathspec] [--upload-dir=dirspec] [--upload-handler=handler] [--upload-limit=limit] [--accept-all] [--accept-none] [--silent] [--debug]DESCRIPTION¶
Haserl is a small cgi wrapper that allows "PHP" style cgi programming, but uses a UNIX bash-like shell or Lua as the programming language. It is very small, so it can be used in embedded environments, or where something like PHP is too big.- It parses POST and GET requests, placing form-elements as name=value pairs into the environment for the CGI script to use. This is somewhat like the uncgi wrapper.
- It opens a shell, and translates all text into printable statements. All text within <% ... %> constructs are passed verbatim to the shell. This is somewhat like writing PHP scripts.
- It can optionally be installed to drop its permissions to the owner of the script, giving it some of the security features of suexec or cgiwrapper.
OPTIONS SUMMARY¶
This is a summary of the command-line options. Please see the OPTIONS section under the long option name for a complete description.OPTIONS¶
- --accept-all
- The program normally accepts POST data only when the
REQUEST_METHOD is POST and only accepts data on the URL data when the
REQUEST_METHOD is GET. This option allows both POST and URL data to be
accepted regardless of the REQUEST_METHOD. When this option is set, the
REQUEST_METHOD takes precedence (e.g. if the method is POST,
FORM_variables are taken from COOKIE data, GET data, and POST data, in
that order. If the method is GET, FORM_variables are taken from COOKIE
data, POST data, and GET data.) The default is not to accept all input
methods - just the COOKIE data and the REQUEST_METHOD.
- --accept-none
- If given, haserl will not parse standard input as http
content before processing the script. This is useful if calling a haserl
script from another haserl script.
- --debug
- Instead of executing the script, print out the script that
would be executed. If the environment variable 'REQUEST_METHOD' is set,
the data is sent with the plain/text content type. Otherwise, the shell
script is printed verbatim.
- --shell=pathspec
- Specify an alternative bash-like shell to use. Defaults to
"/bin/sh"
- --silent
- Haserl normally prints an informational message on error
conditions. This suppresses the error message, so that the use of haserl
is not advertised.
- --upload-dir=dirspec
- Defaults to "/tmp". All uploaded files are
created with temporary filename in this directory HASERL_xxx_path
contains the name of the temporary file. FORM_xxx_name contains the
original name of the file, as specified by the client.
- --upload-handler=pathspec
- When specified, file uploads are handled by this handler,
rather than written to temporary files. The full pathspec must be given
(the PATH is not searched), and the upload-handler is given one
command-line parameter: The name of the FIFO on which the upload file will
be sent. In addition, the handler may receive 3 environment variables:
CONTENT_TYPE, FILENAME, and NAME. These reflect the
MIME content-disposition headers for the content. Haserl will fork the
handler for each file uploaded, and will send the contents of the upload
file to the specified FIFO. Haserl will then block until the handler
terminates. This method is for experts only.
- --upload-limit=limit
- Allow a mime-encoded file up to limit KB to be
uploaded. The default is 0KB (no uploads allowed). Note that
mime-encoding adds 33% to the size of the data.
OVERVIEW OF OPERATION¶
In general, the web server sets up several environment variables, and then uses fork or another method to run the CGI script. If the script uses the haserl interpreter, the following happens:- If haserl is installed suid root, then uid/gid is
set to the owner of the script.
CLIENT SIDE INPUT¶
The haserl interpreter will decode data sent via the HTTP_COOKIE environment variable, and the GET or POST method from the client, and store them as environment variables that can be accessed by haserl. The name of the variable follows the name given in the source, except that a prefix ( FORM_) is prepended. For example, if the client sends "foo=bar", the environment variable is FORM_foo=bar.- NOTE
- When a file is uploaded to the web server, it is stored in
the upload-dir directory. FORM_variable_name= contains the
name of the file uploaded (as specified by the client.)
HASERL_variable_path= contains the name of the file in
upload-dir that holds the uploaded content. To prevent malicious
clients from filling up upload-dir on your web server, file uploads
are only allowed when the --upload-limit option is used to specify
how large a file can be uploaded. Haserl automatically deletes the
temporary file when the script is finished. To keep the file, move it or
rename it somewhere in the script.
- Note that the filename is stored in
HASERL_variable_path This is because the FORM_, GET_, and
POST_ variables are modifiable by the client, and a malicious client can
set a second variable with the name variable_path=/etc/passwd.
Earlier versions did not store the pathspec in HASERL namespace.
To maintain backward compailibility, the name of the temporary file
is also stored in FORM_variable= and
POST_variable=. This is considered unsafe and should not be
used.
LANGUAGE¶
The following language structures are recognized by haserl.- RUN
-
<% [shell script] %>
Anything enclosed by <% %> tags is sent to the sub-shell for execution. The text is sent verbatim.
- INCLUDE
-
<%in pathspec %>
Include another file verbatim in this script. The file is included when the script is initially parsed.
- EVAL
-
<%= expression %>
print the shell expression. Syntactic sugar for "echo expr".
- COMMENT
-
<%# comment %>
Comment block. Anything in a comment block is not parsed. Comments can be nested and can contain other haserl elements.
EXAMPLES¶
- WARNING
- The examples below are simplified to show how to use
haserl. You should be familiar with basic web scripting security
before using haserl (or any scripting language) in a production
environment.
- Simple Command
-
#!/usr/local/bin/haserl content-type: text/plain
- Looping with dynamic output
-
#!/usr/local/bin/haserl Content-type: text/html
- Use Shell defined functions.
-
#!/usr/local/bin/haserl content-type: text/html
- Self Referencing CGI with a form
-
#!/usr/local/bin/haserl content-type: text/html
- Uploading a File
-
#!/usr/local/bin/haserl --upload-limit=4096 --upload-dir=/tmp content-type: text/html
- RFC-2616 Conformance
-
#!/usr/local/bin/haserl <% echo -en "content-type: text/html\r\n\r\n" %> <html><body> ... </body></html>
ENVIRONMENT¶
In addition to the environment variables inherited from the web server, the following environment variables are always defined at startup:- HASERLVER
- haserl version - an informational tag.
- SESSIONID
- A hexadecimal tag that is unique for the life of the CGI (it is generated when the cgi starts; and does not change until another POST or GET query is generated.)
- HASERL_ACCEPT_ALL
- If the --accept-all flag was set, -1, otherwise 0.
- HASERL_SHELL
- The name of the shell haserl started to run sub-shell commands in.
- HASERL_UPLOAD_DIR
- The directory haserl will use to store uploaded files.
- HASERL_UPLOAD_LIMIT
- The number of KB that are allowed to be sent from the
client to the server.
SAFETY FEATURES¶
There is much literature regarding the dangers of using shell to program CGI scripts. haserl contains some protections to mitigate this risk.- Environment Variables
- The code to populate the environment variables is outside
the scope of the sub-shell. It parses on the characters ? and &, so it
is harder for a client to do "injection" attacks. As an example,
foo.cgi?a=test;cat /etc/passwd could result in a variable being
assigned the value test and then the results of running cat
/etc/passwd being sent to the client. Haserl will assign the
variable the complete value: test;cat /etc/passwd
- Privilege Dropping
- If installed as a suid script, haserl will set its
uid/gid to that of the owner of the script. This can be used to have a set
of CGI scripts that have various privilege. If the haserl binary is
not installed suid, then the CGI scripts will run with the uid/gid of the
web server.
- Reject command line parameters given on the URL
- If the URL does not contain an unencoded "=", then the CGI spec states the options are to be used as command-line parameters to the program. For instance, according to the CGI spec: http://192.168.0.1/test.cgi?--upload-limit%3d2000&foo%3dbar
Should set the upload-limit to 2000KB in
addition to setting "Foo=bar". To protect against clients enabling
their own uploads, haserl rejects any command-line options beyond
argv[2]. If invoked as a #! script, the interpreter is argv[0], all
command-line options listed in the #! line are combined into argv[1], and the
script name is argv[2].
LUA¶
If compiled with lua support, --shell=lua will enable lua as the script language instead of bash shell. The environment variables (SCRIPT_NAME, SERVER_NAME, etc) are placed in the ENV table, and the form variables are placed in the FORM table. For example, the self-referencing form above can be written like this:#!/usr/local/bin/haserl --shell=lua content-type: text/html<html><body> <h1>Sample Form</h1> <form action="<% io.write(ENV["SCRIPT_NAME"]) %>" method="GET"> <% # Do some basic validation of FORM_textfield # To prevent common web attacks FORM.textfield=string.gsub(FORM.textfield, "[^%a%d]", "") %> <input type=text name=textfield Value="<% io.write (string.upper(FORM.textfield)) %>" cols=20> <input type=submit value=GO> </form></html> </body>
<% io.write ("Hello World" ) %>Your message is <%= gvar %>-- End of Include file --
#!/usr/local/bin/haserl --shell=lua <% m = haserl.loadfile("bar.lsp") gvar = "Run as m()" m() gvar = "Load and run in one step" haserl.loadfile("bar.lsp")() %>
Hello World Your message is Run as m() -- End of Include file -- Hello World Your message is Load and run in one step -- End of Include file --
LUAC¶
The luac "shell" is a precompiled lua chunk, so interactive editing and testing of scripts is not possible. However, haserl can be compiled with luac support only, and this allows lua support even in a small memory environment. All haserl lua features listed above are still available. (If luac is the only shell built into haserl, the haserl.loadfile is disabled, as the haserl parser is not compiled in.)print ("Content-Type: text/plain0) print ("Your UUID for this run is: " .. ENV.SESSIONID)
luac -o test.luac -s test.lua
echo '#!/usr/bin/haserl --shell=luac' | cat -
test.luac >luac.cgi
#!/usr/bin/haserl --shell=lua --debug Content-Type: text/plain Your UUID for this run is <%= ENV.SESSIONID %>
./test.cgi > test.lua luac -s -o test.luac test.lua echo '#!/usr/bin/haserl --shell=luac' | cat - test.luac >luac.cgi
BUGS¶
Old versions of haserl used <? ?> as token markers, instead of <% %>. Haserl will fall back to using <? ?> if <% does not appear anywhere in the script.NAME¶
The name "haserl" comes from the Bavarian word for "bunny." At first glance it may be small and cute, but haserl is more like the bunny from Monty Python & The Holy Grail. In the words of Tim the Wizard, That's the most foul, cruel & bad-tempered rodent you ever set eyes on!AUTHOR¶
Nathan Angelacos <nangel@users.sourceforge.net>SEE ALSO¶
php(http://www.php.net) uncgi(http://www.midwinter.com/~koreth/uncgi.html) cgiwrapper(http://cgiwrapper.sourceforge.net)October 2010 |