views:

136

answers:

5

This is really just a conceptual question for me at this point.

In Lisp, programs are data and data are programs. The REPL does exactly that - reads and then evaluates.

So how does one go about getting input from the user in a secure way? Obviously it's possible - I mean viaweb - now Yahoo!Stores is pretty secure, so how is it done?

+1  A: 

This is a killer question and I thought this same thing when I was reading about Lisp. Although I haven't done anything meaningful in LISP so my answer is very limited.

What I can tell you is that eval() is nasty. There is a saying that I like "If eval is the answer then you are asking the wrong question." --Unknown.

If the attacker can control data that is then evaluated then you have a very serious remote code execution vulnerability. This can be mitigated, and I'll show you an example with PHP, because that is what I know:

$id=addslashes($_GET['id']);
eval('$test="$id";');

If you weren't doing an add slashes then an attacker could get remote code execution by doing this:

http://localhost?evil_eval.php?id="; phpinfo();/*

But the add slashes will turn the " into a \", thus keeping the attacker from "breaking out" of the "data" and being able to execute code. Which is very similar to sql injection.

Rook
+1 for the quote. That probably applies to most any language. And of course any time I use php, input is always wrapped in an htmlspecialchars()
Wayne Werner
+13  A: 

The REPL stands for Read Eval Print Loop.

(loop (print (eval (read))))

Above is only conceptual, the real REPL code is much more complicated (with error handling, debugging, ...).

You can read all kinds of data in Lisp without evaluating it. Evaluation is a separate step - independent from reading data.

There are all kinds of IO functions in Lisp. The most complex of the provided functions is usually READ, which reads s-expressions. There is an option in Common Lisp which allows evaluation during READ, but that can and should be turned off when reading data.

So, data in Lisp is not necessarily a program and even if data is a program, then Lisp can read the program as data - without evaluation. A REPL should only be used by a developer and should not be exposed to arbitrary users. For getting data from users one uses the normal IO functions, including functions like READ, which can read S-expressions, but does not evaluate them.

Here are a few things one should NOT do:

  • use READ to read arbitrary data. READ for examples allows one to read really large data - there is no limit.

  • evaluate during READ ('read eval'). This should be turned off.

  • read symbols from I/O and call their symbol functions

  • read cyclical data structures with READ, when your functions expect plain lists. Walking down a cyclical list can keep your program busy for a while.

  • not handle syntax errors during reading from data.

Rainer Joswig
+1 good answer.
Rook
+4  A: 

You do it the way everyone else does it. You read a string of data from the stream, you parse it for your commands and parameters, you validate the commands and parameters, and you interpret the commands and parameters.

There's no magic here.

Simply put, what you DON'T do, is you don't expose your Lisp listener to an unvalidated, unsecure data source.

As was mentioned, the REPL is read - eval - print. @The Rook focused on eval (with reason), but do not discount READ. READ is a VERY powerful command in Common Lisp. The reader can evaluate code on its own, before you even GET to "eval".

Do NOT expose READ to anything you don't trust.

With enough work, you could make a custom package, limit scope of functions avaliable to that package, etc. etc. But, I think that's more work than simply writing a simple command parser myself and not worrying about some side effect that I missed.

Will Hartung
That's exactly where my worry is - that the reader can evaluate code. Is there some type of equivalent to the Python 2.6 `raw_input()`?
Wayne Werner
http://www.lispworks.com/documentation/HyperSpec/Body/f_rd_lin.htm
Ken
+1  A: 

I found that question quit controversial. The eval wont eval your input unless you explicitly ask for it. I mean your input will not be treat it as a LISP code but instead as a string.

Is not because that your language have powerfull concept like the eval that it is not "safe".

I think the confusion come from SQL where your actually treat an input as a [part of] SQL.

(query (concatenate 'string "SELECT * FROM foo WHERE id = " input-id))

Here input-id is being evaluate by the SQL engine. This is because you have no nice way to write SQL, or whatever, but the point is that your input become part of what is being evaluate.

So eval don't bring you insecurity unless your are using it eyes closed.

EDIT Forgot to tell that this apply to any language.

mathk
+2  A: 
  1. Create your own readtable and fill with necessary hooks: SET-MACRO-CHARACTER, SET-DISPATCH-MACRO-CHARACTER et al.
  2. Bind READTABLE to your own readtable.
  3. Bind READ-EVAL to nil to prevent #. (may not be necessary if step 1 is done right)
  4. READ

Probably something else.

Also there is a trick in interning symbols in temporary package while reading.

If data in not LL(1)-ish, simply write usual parser.

adobriyan