10 Security
Writing servers is an inherently dangerous job that should be carried out with some considerations. You have basically started a program on a public terminal and invited strangers to use it. When using the interactive server or inetd based server the server runs under your privileges. Using CGI scripted it runs with the privileges of your web-server. Though it should not be possible to fatally compromise a Unix machine using user privileges, getting unconstrained access to the system is highly undesirable.
Symbolic languages have an additional handicap in their inherent possibilities to modify the running program and dynamically create goals (this also applies to the popular Perl and PHP scripting languages). Here are some guidelines.
- Check your input
Hardly anything can go wrong if you check the validity of query-arguments before formulating an answer. - Check filenames
If part of the query consists of filenames or directories, check them. This also applies to files you only read. Passing names as/etc/passwd
, but also../../../../../etc/passwd
are tried by hackers to learn about the system they want to attack. So, expand provided names using absolute_file_name/[2,3] and verify they are inside a folder reserved for the server. Avoid symbolic links from this subtree to the outside world. The example below checks validity of filenames. The first call ensures proper canonisation of the paths to avoid an mismatch due to symbolic links or other filesystem ambiguities.check_file(File) :- absolute_file_name('/path/to/reserved/area', Reserved), absolute_file_name(File, Tried), sub_atom(Tried, 0, _, _, Reserved).
- Check scripts
Should input in any way activate external scripts using shell/1 oropen(pipe(Command), ...)
, verify the argument once more. Use process_create/3 in preference over shell/1 as this function avoids stringification of arguments (Unix) or ensures proper quoting of arguments (Windows). - Check meta-calling
The attractive situation for you and your attacker is below:reply(Query) :- member(search(Args), Query), member(action=Action, Query), member(arg=Arg, Query), call(Action, Arg). % NEVER EVER DO THIS!
All your attacker has to do is specify Action as
shell
and Arg as/bin/sh
and he has an uncontrolled shell!