<?xml version="1.0" encoding="utf-8"?>
<?xml-model href="rfc7991bis.rnc"?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>

<rfc
  xmlns:xi="http://www.w3.org/2001/XInclude"
  category="bcp"
  docName="draft-cameron-secure-icap-bcp-00"
  ipr="trust200902"
  obsoletes=""
  updates=""
  submissionType="IETF"
  xml:lang="en"
  version="3">

  <front>
    <title abbrev="Secure ICAP BCP">Best Practices for Secure ICAP Deployment with Credential-Based TLS</title>
    
    <seriesInfo name="Internet-Draft" value="draft-cameron-secure-icap-bcp-00"/>
    
    <author fullname="Ross W.D. Cameron" initials="R.W.D." surname="Cameron">
      <organization>Prometheus Systems</organization>
      <address>
        <email>rwd.cameron@prometheus-systems.co.za</email>
      </address>
    </author>
    
    <date year="2025" month="June" day="26"/>
    
    <keyword>ICAP</keyword>
    <keyword>TLS</keyword>
    <keyword>security</keyword>
    <keyword>authentication</keyword>
    
    <abstract>
      <t>
        This document defines a Best Current Practice for deploying the
        Internet Content Adaptation Protocol (ICAP) in a secure manner. It
        introduces a mechanism for symmetric encryption using TLS, with
        client authentication based on user credentials. The aim is to
        provide confidentiality and integrity of ICAP traffic in modern
        zero-trust and content-sensitive environments.
      </t>
    </abstract>
  </front>

  <middle>
    <section numbered="true" toc="default">
      <name>Introduction</name>
      <t>
        ICAP <xref target="RFC3507" format="default"/> provides offloading of content adaptation to dedicated
        servers but lacks native support for transport-layer security and
        authentication. This document offers operational guidance for
        deploying ICAP securely using TLS with short-lived credential-based
        sessions to enable multi-tenant ICAP services.
      </t>
    </section>

    <section numbered="true" toc="default">
      <name>Scope</name>
      <t>
        This BCP is intended for ICAP implementers and administrators
        deploying ICAP within security-sensitive environments where plaintext
        transport and persistent sessions are undesirable. This is particularly
        applicable to public ICAP services serving multiple customers.
      </t>
    </section>

    <section numbered="true" toc="default">
      <name>Authentication and Key Derivation</name>
      <t>
        Clients MUST authenticate using a username/password mechanism encoded
        via HTTP Basic Authorization header. This credential pair MUST be
        used to derive a 256-bit session key using PBKDF2 with SHA-256.
      </t>
      <t>
        The derived session key serves as an additional authentication layer
        beyond TLS transport security, enabling multi-tenant session isolation
        and per-customer access control within the ICAP service.
      </t>
    </section>

    <section numbered="true" toc="default">
      <name>TLS Enforcement</name>
      <t>
        ICAP servers MUST accept connections only over TLS 1.2 or newer.
        The recommended port for secure ICAP ("ICAPS") is 11344.
        Non-encrypted connections SHOULD be rejected by default.
      </t>
    </section>

    <section numbered="true" toc="default">
      <name>Session Expiry</name>
      <t>
        Session keys and associated TLS sessions MUST expire no later than
        one (1) hour after creation. Clients SHOULD re-authenticate upon
        session renewal.
      </t>
    </section>

    <section numbered="true" toc="default">
      <name>ICAP Header Constraints</name>
      <t>
        Implementations MUST include the Authorization header during the
        initial handshake. TLS renegotiation SHOULD NOT be used. Session
        rekeying MUST be triggered by explicit re-authentication.
      </t>
    </section>

    <section numbered="true" toc="default">
      <name>Operational Guidance</name>
      <ul spacing="normal">
        <li>
          Session caching MUST be scoped per client based on the derived
          session key and authentication credentials.
        </li>
        <li>
          Salt and iteration count for PBKDF2 SHOULD be dynamically generated
          by the server and delivered via TLS extension parameters.
        </li>
        <li>
          Clients SHOULD validate server certificates using pinned keys or a
          trusted CA.
        </li>
        <li>
          To prevent downgrade attacks, the Options response MAY advertise
          "Require-TLS: yes".
        </li>
      </ul>
    </section>

    <section numbered="true" toc="default">
      <name>Security Considerations</name>
      <ul spacing="normal">
        <li>Weak password policies MUST be avoided.</li>
        <li>TLS session resumption SHOULD be disabled.</li>
        <li>Authorization headers MUST NOT be logged or echoed in diagnostics.</li>
      </ul>
    </section>

    <section numbered="true" toc="default">
      <name>IANA Considerations</name>
      <t>
        No new IANA actions are required by this document.
      </t>
    </section>
  </middle>

  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
        <reference anchor="RFC3507" target="https://www.rfc-editor.org/info/rfc3507">
          <front>
            <title>Internet Content Adaptation Protocol (ICAP)</title>
            <author initials="J." surname="Elson" fullname="J. Elson">
              <organization/>
            </author>
            <author initials="A." surname="Cerpa" fullname="A. Cerpa">
              <organization/>
            </author>
            <date year="2003" month="April"/>
          </front>
          <seriesInfo name="RFC" value="3507"/>
          <seriesInfo name="DOI" value="10.17487/RFC3507"/>
        </reference>
      </references>
    </references>

    <section numbered="false" toc="default">
      <name>Appendix A. Example: ICAP over Secure TLS</name>
      <artwork name="" type="" align="left" alt=""><![CDATA[
C: CONNECT icap.example.com:11344
S: <TLS Handshake>
C: Authorization: Basic <base64-encoded credentials>
...
<ICAP request begins within TLS session>
]]></artwork>
    </section>

    <section numbered="false" toc="default">
      <name>Appendix B. Sample Implementation (Pseudo-code)</name>
      
      <section numbered="false" toc="default">
        <name>Client Implementation</name>
        <artwork name="" type="" align="left" alt=""><![CDATA[
function connectToSecureICAPServer(username, password):
    tlsSocket = TLS.connect("icap.example.com", 11344, 
                           verifyCertificates=True)

    credentials = base64Encode(username + ":" + password)
    authHeader = "Authorization: Basic " + credentials

    // Prepare ICAP OPTIONS request
    request = ICAPRequest()
    request.method = "OPTIONS"
    request.uri = "icap://icap.example.com/reqmod"
    request.headers["Authorization"] = authHeader

    tlsSocket.send(request.serialize())

    response = tlsSocket.receive()
    if response.statusCode == 200:
        print("Secure session established.")
        startSessionTimeout(1 hour)
    else if response.statusCode == 407:
        print("Authentication failed.")
        tlsSocket.close()
]]></artwork>
      </section>

      <section numbered="false" toc="default">
        <name>Server Implementation</name>
        <artwork name="" type="" align="left" alt=""><![CDATA[
function handleSecureICAPConnection(tlsSocket):
    request = parseICAPRequest(tlsSocket.receive())

    if not isValidTLS(tlsSocket):
        tlsSocket.send(ICAPErrorResponse(403))
        return

    if "Authorization" not in request.headers:
        tlsSocket.send(ICAPAuthChallenge(407))
        return

    credentials = parseBasicAuth(request.headers["Authorization"])
    if not validateUser(credentials.username, credentials.password):
        tlsSocket.send(ICAPAuthChallenge(407))
        return

    sessionKey = deriveKeyPBKDF2(
        password=credentials.password,
        salt=generateServerSalt(),
        iterations=100000,
        keyLength=256
    )

    // Mark session valid for 1 hour based on session key
    createSecureSession(sessionKey, ttl=1 hour)

    tlsSocket.send(ICAPSuccessResponse(200))
]]></artwork>
      </section>
    </section>
  </back>
</rfc>
