Home    |    View Topics    |    Search    |    Contact Us    |   



Category:   Application (Web Server/CGI)  >   Apache HTTPD Vendors:   Apache Software Foundation
Apache mod_disk_cache Stores Authentication Credentials on Disk
SecurityTracker Alert ID:  1009509
SecurityTracker URL:
CVE Reference:   GENERIC-MAP-NOMATCH   (Links to External Site)
Date:  Mar 21 2004
Impact:   Disclosure of authentication information

Version(s): 2.0.49 and prior versions
Description:   A vulnerability was reported in Apache mod_disk_cache. The web server stores cached authentication credentials on the disk, possibly including plaintext passwords.

Andreas Steinmetz reported that if mod_disk_cache is enabled, 'modules/experimental/mod_disk_cache.c' will write headers, including client authentication credentials, to the disk.

In the case of HTTP Basic Authentication, plaintext passwords will be stored on the disk, the report said.

The vendor was reportedly notified on March 2, 2004.

Impact:   A local user may be able to obtain cached authentication credentials.
Solution:   No solution was available at the time of this entry. An unofficial patch is provided in the Source Message.
Vendor URL: (Links to External Site)
Cause:   Access control error
Underlying OS:  Linux (Any), UNIX (Any), Windows (Any)

Message History:   None.

 Source Message Contents

Subject:  Apache mod_disk_cache stores client authentication credentials on

Content-Type: text/plain; charset=us-ascii; format=flowed
Content-Transfer-Encoding: 7bit

Hash: SHA1


mod_disk_cache stores all client authentication credentials for cached
objects on disk. This means proxy authentication credentials as well as
in certain RFC2616 defined cases standard authentication credentials.

In case of Basic Authentication *plaintext passwords* are stored on disk.


Apache 2.0.48, 2.0.49 and probably lots of earlier 2.0.x versions, if
mod_disk_cache is used.

This affects especially sites which use Apache 2.0.x as a proxy with
proxy authentication and that have a disk cache configured.

Vendor Status:

The Apache team was notified via on March 2nd and
3rd, 2004. There was some communication and I did supply a patch to fix
the proxy authentication caching problem on March 7th. The fix, however,
is not included in Apache 2.0.49.

Actually I did plan to release this information on April 3rd, but as the
Apache 2.0.49 release is out without a fix and as I wasn't contacted by
the Apache team since March 7th I do assume that the problem is not
going to be corrected. So I decided to release this information today.


~From modules/experimental/mod_disk_cache.c, function write_headers(),
line 641 of apache 2.0.48 or line 598 of apache 2.0.49 (lines wrapped
and shortened):

/* Parse the vary header and dump those fields from the headers_in. */
/* Make call to the same thing cache_select_url calls to crack Vary. */
/* @@@ Some day, not today. */
if (r->headers_in) {
~    int i;
~    apr_table_entry_t *elts = (apr_table_entry_t *)
~       apr_table_elts(r->headers_in)->elts;
~    for (i = 0; i < apr_table_elts(r->headers_in)->nelts; ++i) {
~        if (elts[i].key != NULL) {
~            buf = apr_pstrcat(r->pool, elts[i].key, ": ",  elts[i].val,
~                  CRLF, NULL);
~            amt = strlen(buf);
~            apr_file_write(hfd, buf, &amt);
~        }
~    }
~    buf = apr_pstrcat(r->pool, CRLF, NULL);
~    amt = strlen(buf);
~    apr_file_write(hfd, buf, &amt);

So all r->headers_in headers are written to disk. These headers are the
complete header set as presented by the client which naturally includes
all client authentication credentials.

Note that these stored headers are later used only to handle the VARY
header for content negotiation.

Excerpt from RFC 2616 - Hypertext Transfer Protocol -- HTTP/1.1:

13.5.1 End-to-end and Hop-by-hop Headers

~   For the purpose of defining the behavior of caches and non-caching
~   proxies, we divide HTTP headers into two categories:

~      - End-to-end headers, which are  transmitted to the ultimate
~        recipient of a request or response. End-to-end headers in
~        responses MUST be stored as part of a cache entry and MUST be
~        transmitted in any response formed from a cache entry.

~      - Hop-by-hop headers, which are meaningful only for a single
~        transport-level connection, and are not stored by caches or
~        forwarded by proxies.

~   The following HTTP/1.1 headers are hop-by-hop headers:

~      - Connection
~      - Keep-Alive
~      - Proxy-Authenticate
~      - Proxy-Authorization
~      - TE
~      - Trailers
~      - Transfer-Encoding
~      - Upgrade

So the proxy authentication headers cannot be used for content
negotiation. Still they are stored on disk. This clearly violates the
'are not stored by caches' of RFC2616.

It is good security practice never to store authentication credentials
presented by a client on disk. Hopefully the Apache team will adopt this

I do attach the simple patch that I did send to the Apache team adapted
to Apache 2.0.49 (just offsets to the 2.0.48 version) to fix this
security problem for proxy authentication credentials and make Apache
conform to RFC2616.
Note that there may still be cached standard client authentication
credentials after applying this patch for cases where RFC2616 allows
caching of such objects.
- --
Andreas Steinmetz

Version: GnuPG v1.2.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla -


Content-Type: text/plain;
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;

diff -rNu httpd-2.0.49.orig/modules/experimental/cache_util.c httpd-2.0.49/modules/experimental/cache_util.c
--- httpd-2.0.49.orig/modules/experimental/cache_util.c	2004-02-09 21:53:16.000000000 +0100
+++ httpd-2.0.49/modules/experimental/cache_util.c	2004-03-20 15:55:51.000000000 +0100
@@ -516,3 +516,25 @@
     apr_table_unset(headers_out, "Upgrade");
     return headers_out;
+/* Create a new table consisting of those elements from a request_rec's
+ * headers_in that are allowed to be stored in a cache.
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_in(request_rec *r)
+    /* Make a copy of the request headers, and remove from
+     * the copy any hop-by-hop headers, as defined in Section
+     * 13.5.1 of RFC 2616
+     */
+    apr_table_t *headers_in;
+    headers_in = apr_table_copy(r->pool, r->headers_in);
+    apr_table_unset(headers_in, "Connection");
+    apr_table_unset(headers_in, "Keep-Alive");
+    apr_table_unset(headers_in, "Proxy-Authenticate");
+    apr_table_unset(headers_in, "Proxy-Authorization");
+    apr_table_unset(headers_in, "TE");
+    apr_table_unset(headers_in, "Trailers");
+    apr_table_unset(headers_in, "Transfer-Encoding");
+    apr_table_unset(headers_in, "Upgrade");
+    return headers_in;
diff -rNu httpd-2.0.49.orig/modules/experimental/mod_cache.h httpd-2.0.49/modules/experimental/mod_cache.h
--- httpd-2.0.49.orig/modules/experimental/mod_cache.h	2004-02-09 21:53:16.000000000 +0100
+++ httpd-2.0.49/modules/experimental/mod_cache.h	2004-03-20 15:55:51.000000000 +0100
@@ -238,6 +238,11 @@
 CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_out(apr_pool_t *pool, apr_table_t *t);
+/* Create a new table consisting of those elements from a request_rec's
+ * headers_in that are allowed to be stored in a cache
+ */
+CACHE_DECLARE(apr_table_t *)ap_cache_cacheable_hdrs_in(request_rec *r);
  * cache_storage.c
diff -rNu httpd-2.0.49.orig/modules/experimental/mod_disk_cache.c httpd-2.0.49/modules/experimental/mod_disk_cache.c
--- httpd-2.0.49.orig/modules/experimental/mod_disk_cache.c	2004-02-09 21:53:16.000000000 +0100
+++ httpd-2.0.49/modules/experimental/mod_disk_cache.c	2004-03-20 15:55:51.000000000 +0100
@@ -600,8 +600,9 @@
 	/* @@@ Some day, not today. */
         if (r->headers_in) {
             int i;
-            apr_table_entry_t *elts = (apr_table_entry_t *) apr_table_elts(r->headers_in)->elts;
-            for (i = 0; i < apr_table_elts(r->headers_in)->nelts; ++i) {
+            apr_table_t* headers_in = ap_cache_cacheable_hdrs_in(r);
+            apr_table_entry_t *elts = (apr_table_entry_t *) apr_table_elts(headers_in)->elts;
+            for (i = 0; i < apr_table_elts(headers_in)->nelts; ++i) {
                 if (elts[i].key != NULL) {
                     buf = apr_pstrcat(r->pool, elts[i].key, ": ",  elts[i].val, CRLF, NULL);
                     amt = strlen(buf);



Go to the Top of This SecurityTracker Archive Page

Home   |    View Topics   |    Search   |    Contact Us

This web site uses cookies for web analytics. Learn More

Copyright 2019, LLC