And just in case it gets edited by someone else before you have a chance to read it, I've copied and pasted the current version below for reference. Correction/comments/questions all welcome.
;; WARNING: You SHOULD NOT use clojure.core/read or clojure.core/read-string to
;; read data from untrusted sources. They were designed only for reading Clojure
;; code and data from trusted sources (e.g. files that you know you wrote
;; yourself, and no one else has permission to modify them).
;; Instead, either:
;; (a) use another data serialization format such as JSON, XML, etc. and a
;; library for reading them that you trust not to have vulnerabilities, or
;; (b) if you want a serialization format that can be read safely and looks like
;; Clojure data structures, use edn (
https://github.com/edn-format/edn), for
;; which Clojure 1.5 has functions clojure.edn/read and clojure.edn/read-string
;; to read them safely.
;; You definitely should not use clojure.core/read or read-string if *read-eval*
;; has its default value of true, because an attacker could cause your
;; application to execute arbitrary code while it is reading. Example:
user=> (read-string "#=(clojure.java.shell/sh \"echo\" \"hi\")")
{:exit 0, :out "hi\n", :err ""}
;; It is straightforward to modify the example above into more destructive
;; ones that remove all of your files, copy them to someone else's computer
;; over the Internet, install Trojans, etc.
;; Even if you do bind *read-eval* to false first, like so:
(defn read-string-unsafely [s]
(binding [*read-eval* false]
(read-string s)))
;; you may hope you are safe reading untrusted data that way, but in Clojure 1.4
;; and earlier, an attacker can send data that causes your system to execute
;; arbitrary Java constructors. Most of these are benign, but it only takes one
;; to ruin your application's day. Examples that should scare you:
;; This causes a socket to be opened, as long as the JVM
;; sandboxing allows it.
(read-string-unsafely "#java.net.Socket[\"
www.google.com\" 80]")
;; This causes precious-file.txt to be created if it doesn't
;; exist, or if it does exist, its contents will be erased (given
;; appropriate JVM sandboxing permissions, and underlying OS file
;; permissions).
(read-string-unsafely "#java.io.FileWriter[\"precious-file.txt\"]")
;; The particular issue of executing arbitrary Java constructors used in the
;; examples above no longer works in Clojure 1.5 when *read-eval* is false.
;; Even so, you SHOULD NEVER USE clojure.core/read or clojure.core/read-string
;; for reading untrusted data.
;; If you understand all of the above, and want to use read or read-string to
;; read data from a _trusted_ source, continue on below.
;; read wants *in* set to a java.io.PushbackReader.
;; with-open sets *in* and closes it after it's done.
;; *read-eval* specifies whether to evaluate #=() forms
;; when reading.
(defn read-from-file-with-trusted-contents [filename]
(with-open
[r (java.io.PushbackReader.
(
clojure.java.io/reader filename))]
(binding [*read-eval* false]
(read r))))
user=> (spit "testfile.txt" "{:a 1 :b 2 :c 3}")
nil
user=> (read-from-file-with-trusted-contents "testfile.txt")
{:a 1, :b 2, :c 3}