diff --git a/NEWS.txt b/NEWS.txt
index 7a940d9b264f3f607caeae2545008ba283cdc612..1902a3084f5c6a8fb14d096c8a8ce8cadce077be 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -1,4 +1,21 @@
 
+Whats new in ZODB 3.8.2
+=======================
+
+Bugs Fixed:
+
+- Fixed vulnerabilities in the ZEO network protocol that allow:
+
+  - CVE-2009-0668 Arbitrary Python code execution in ZODB ZEO storage servers
+  - CVE-2009-0669 Authentication bypass in ZODB ZEO storage servers
+
+  The vulnerabilities only apply if you are using ZEO to share a
+  database among multiple applications or application instances and if
+  untrusted clients are able to connect to your ZEO servers.
+
+- Limit the number of object ids that can be allocated at once to
+  avoid running out of memory.
+
 Whats new in ZODB 3.8.1
 =======================
 
diff --git a/setup.py b/setup.py
index 7048badc38ac48afb841af49444bcaac372d815f..3a7f7bb998a36927cdad79b915018aca3d1e005f 100644
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,7 @@ to application logic.  ZODB includes features such as a plugable storage
 interface, rich transaction support, and undo.
 """
 
-VERSION = "3.8.1"
+VERSION = "3.8.2"
 
 # The (non-obvious!) choices for the Trove Development Status line:
 # Development Status :: 5 - Production/Stable
diff --git a/src/ZEO/StorageServer.py b/src/ZEO/StorageServer.py
index 3653cbe373f4d69e52f6e8e1c126f5c9d561d98c..0d81c5b06c4051b034e9031768f436570471d768 100644
--- a/src/ZEO/StorageServer.py
+++ b/src/ZEO/StorageServer.py
@@ -111,7 +111,7 @@ class ZEOStorage:
         for func in self.extensions:
             self._extensions[func.func_name] = None
 
-    def finish_auth(self, authenticated):
+    def _finish_auth(self, authenticated):
         if not self.auth_realm:
             return 1
         self.authenticated = authenticated
@@ -421,6 +421,7 @@ class ZEOStorage:
 
     def new_oids(self, n=100):
         """Return a sequence of n new oids, where n defaults to 100"""
+        n = min(n, 100)
         if self.read_only:
             raise ReadOnlyError()
         if n <= 0:
diff --git a/src/ZEO/auth/auth_digest.py b/src/ZEO/auth/auth_digest.py
index fef0af1474e344777a81e9372c6f8a6e4bd0c223..a440cce4449aa4604baf2b85d7110aad402cf9de 100644
--- a/src/ZEO/auth/auth_digest.py
+++ b/src/ZEO/auth/auth_digest.py
@@ -121,7 +121,7 @@ class StorageClass(ZEOStorage):
         check = hexdigest("%s:%s" % (h_up, challenge))
         if check == response:
             self.connection.setSessionKey(session_key(h_up, self._key_nonce))
-        return self.finish_auth(check == response)
+        return self._finish_auth(check == response)
 
     extensions = [auth_get_challenge, auth_response]
 
diff --git a/src/ZEO/tests/auth_plaintext.py b/src/ZEO/tests/auth_plaintext.py
index 50d4bbdeec29c3e5ea97f943273e81092961e7e1..a5ba209399bb59f30bc017e3830caac76c88f589 100644
--- a/src/ZEO/tests/auth_plaintext.py
+++ b/src/ZEO/tests/auth_plaintext.py
@@ -41,7 +41,7 @@ class StorageClass(ZEOStorage):
             self.connection.setSessionKey(session_key(username,
                                                       self.database.realm,
                                                       password))
-        return self.finish_auth(dbpw == password_dig)
+        return self._finish_auth(dbpw == password_dig)
 
 class PlaintextClient(Client):
     extensions = ["auth"]
diff --git a/src/ZEO/zrpc/connection.py b/src/ZEO/zrpc/connection.py
index 2a7a02d130755b4bbed92cbd7b85b6029c2dccf0..57f689b9f4d8854644b49f987ecf7b4a6357eab7 100644
--- a/src/ZEO/zrpc/connection.py
+++ b/src/ZEO/zrpc/connection.py
@@ -24,7 +24,7 @@ import traceback, time
 import ThreadedAsync
 from ZEO.zrpc import smac
 from ZEO.zrpc.error import ZRPCError, DisconnectedError
-from ZEO.zrpc.marshal import Marshaller
+from ZEO.zrpc.marshal import Marshaller, ServerMarshaller
 from ZEO.zrpc.trigger import trigger
 from ZEO.zrpc.log import short_repr, log
 from ZODB.loglevels import BLATHER, TRACE
@@ -883,6 +883,7 @@ class ManagedServerConnection(Connection):
     def __init__(self, sock, addr, obj, mgr):
         self.mgr = mgr
         self.__super_init(sock, addr, obj, 'S')
+        self.marshal = ServerMarshaller()
         self.obj.notifyConnected(self)
 
     def handshake(self):
diff --git a/src/ZEO/zrpc/marshal.py b/src/ZEO/zrpc/marshal.py
index 4a65c090dfb3e8425703e84d1ae3bfaea08d6b2f..6c3379e290c55ed5bbdee2fec3cbaba4475b9186 100644
--- a/src/ZEO/zrpc/marshal.py
+++ b/src/ZEO/zrpc/marshal.py
@@ -52,6 +52,20 @@ class Marshaller:
                 level=logging.ERROR)
             raise
 
+class ServerMarshaller(Marshaller):
+
+    def decode(self, msg):
+        """Decodes msg and returns its parts"""
+        unpickler = cPickle.Unpickler(StringIO(msg))
+        unpickler.find_global = server_find_global
+
+        try:
+            return unpickler.load() # msgid, flags, name, args
+        except:
+            log("can't decode message: %s" % short_repr(msg),
+                level=logging.ERROR)
+            raise
+
 _globals = globals()
 _silly = ('__doc__',)
 
@@ -78,3 +92,21 @@ def find_global(module, name):
         return r
 
     raise ZRPCError("Unsafe global: %s.%s" % (module, name))
+
+def server_find_global(module, name):
+    """Helper for message unpickler"""
+    try:
+        m = __import__(module, _globals, _globals, _silly)
+    except ImportError, msg:
+        raise ZRPCError("import error %s: %s" % (module, msg))
+
+    try:
+        r = getattr(m, name)
+    except AttributeError:
+        raise ZRPCError("module %s has no global %s" % (module, name))
+
+    safe = getattr(r, '__no_side_effects__', 0)
+    if safe:
+        return r
+
+    raise ZRPCError("Unsafe global: %s.%s" % (module, name))