Use string.Template.safe_substitute for HTTP exc body templates
diff --git a/CHANGES.txt b/CHANGES.txt
index feddb17..0fb36f9 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -63,6 +63,12 @@
 Bugfix
 ~~~~~~
 
+- Exceptions now use string.Template.safe_substitute rather than
+  string.Template.substitute. The latter would raise for missing mappings, the
+  former will simply not substitute the missing variable. This is safer in case
+  the WSGI environ does not contain the keys necessary for the body template.
+  See https://github.com/Pylons/webob/issues/345.
+
 - Request.host_url, Request.host_port, Request.domain correctly parse IPv6 Host
   headers as provided by a browser. See
   https://github.com/Pylons/webob/pull/332
diff --git a/src/webob/exc.py b/src/webob/exc.py
index d0b04d2..553a0b1 100644
--- a/src/webob/exc.py
+++ b/src/webob/exc.py
@@ -301,7 +301,7 @@
             for k, v in self.headers.items():
                 args[k.lower()] = escape(v)
         t_obj = self.body_template_obj
-        return t_obj.substitute(args)
+        return t_obj.safe_substitute(args)
 
     def plain_body(self, environ):
         body = self._make_body(environ, no_escape)