Skip to content

Privacy Gateway: a privacy preserving proxy built on Internet standards

Privacy Gateway: a privacy preserving proxy built on Internet standards

If you’re running a privacy-oriented application or service on the Internet, your options to provably protect users’ privacy are limited. You can minimize logs and data collection but even then, at a network level, every HTTP request needs to come from somewhere. Information generated by HTTP requests, like users’ IP addresses and TLS fingerprints, can be sensitive especially when combined with application data.

Meaningful improvements to your users’ privacy require a change in how HTTP requests are sent from client devices to the server that runs your application logic. This was the motivation for Privacy Gateway: a service that relays encrypted HTTP requests and responses between a client and application server. With Privacy Gateway, Cloudflare knows where the request is coming from, but not what it contains, and applications can see what the request contains, but not where it comes from. Neither Cloudflare nor the application server has the full picture, improving end-user privacy.

We recently deployed Privacy Gateway for Flo Health, a period tracking app, for the launch of their Anonymous Mode. With Privacy Gateway in place, all request data for Anonymous Mode users is encrypted between the app user and Flo, which prevents Flo from seeing the IP addresses of those users and Cloudflare from seeing the contents of that request data.

With Privacy Gateway in place, several other privacy-critical applications are possible:

  • Browser developers can collect user telemetry in a privacy-respecting manner– what extensions are installed, what defaults a user might have changed — while removing what is still a potentially personal identifier (the IP address) from that data.
  • Users can visit a healthcare site to report a Covid-19 exposure without worrying that the site is tracking their IP address and/or location.
  • DNS resolvers can serve DNS queries without linking who made the request with what website they’re visiting – a pattern we’ve implemented with Oblivious DNS.
    Privacy Gateway is based on Oblivious HTTP (OHTTP), an emerging IETF standard and is built upon standard hybrid public-key cryptography.

How does it work?

The main innovation in the Oblivious HTTP standard – beyond a basic proxy service – is that these messages are encrypted to the application’s server, such that Privacy Gateway learns nothing of the application data beyond the source and destination of each message.

Privacy Gateway enables application developers and platforms, especially those with strong privacy requirements, to build something that closely resembles a “Mixnet”: an approach to obfuscating the source and destination of a message across a network. To that end, Privacy Gateway consists of three main components:

  • Client: the user’s device, or any client that’s configured to forward requests to Privacy Gateway.
  • Privacy Gateway: a service operated by Cloudflare and designed to relay requests between the Client and the Gateway, without being able to observe the contents within.
  • Application server: the origin or application web server responsible for decrypting requests from clients, and encrypting responses back.
  • If you were to imagine request data as the contents of the envelope (a letter) and the IP address and request metadata as the address on the outside, with Privacy Gateway, Cloudflare is able to see the envelope’s address and safely forward it to its destination without being able to see what’s inside.

    An Oblivious HTTP transaction using Privacy Gateway

    In slightly more detail, the data flow is as follows:

  • Client encapsulates an HTTP request using the public key of the application server, and sends it to Privacy Gateway over an HTTPS connection.
  • Privacy Gateway forwards the request to the server over its own, separate HTTPS connection with the application server.
  • The application server  decapsulates the request, forwarding it to the target server which can produce the response.
  • The application server returns an encapsulated response to Privacy Gateway, which then forwards the result to the client.
    As specified in the protocol, requests from the client to the server are encrypted using HPKE, a state-of-the-art standard for public key encryption – which you can read more about here. We’ve taken additional measures to ensure that OHTTP’s use of HPKE is secure by conducting a formal analysis of the protocol, and we expect to publish a deeper analysis in the coming weeks.
  • How Privacy Gateway improves end-user privacy

    This interaction offers two types of privacy, which we informally refer to as request privacy and client privacy.

    Request privacy means that the application server does not learn information that would otherwise be revealed by an HTTP request, such as IP address, geolocation, TLS and HTTPS fingerprints, and so on. Because Privacy Gateway uses a separate HTTPS connection between itself and the application server, all of this per-request information revealed to the application server represents that of Privacy Gateway, not of the client. However, developers need to take care to not send personally identifying information in the contents of requests. If the request, once decapsulated, includes information like users’ email, phone number, or credit card info, for example, Privacy Gateway will not meaningfully improve privacy.

    Client privacy is a stronger notion. Because Cloudflare and the application server are not colluding to share individual user’s data, from the server’s perspective, each individual transaction came from some unknown client behind Privacy Gateway. In other words, a properly configured Privacy Gateway deployment means that applications cannot link any two requests to the same client. In particular, with Privacy Gateway, privacy loves company. If there is only one end-user making use of Privacy Gateway, then it only provides request privacy (since the client IP address remains hidden from the Gateway). It would not provide client privacy, since the server would know that each request corresponds to the same, single client. Client privacy requires that there be many users of the system, so the application server cannot make this determination.

    To better understand request and client privacy, consider the following HTTP request between a client and server:

    Normal HTTP configuration with a client anonymity set of size 1

    If a client connects directly to the server (or “Gateway” in OHTTP terms), the server is likely to see information about the client, including the IP address, TLS cipher used, and a degree of location data based on that IP address:

    - ipAddress: 192.0.2.33 # the client’s real IP address
    - ASN: 7922
    - AS Organization: Comcast Cable
    - tlsCipher: AEAD-CHACHA20-POLY1305-SHA256 # potentially unique
    - tlsVersion: TLSv1.3
    - Country: US
    - Region: California
    - City: Campbell
    

    There’s plenty of sensitive information here that might be unique to the end-user. In other words, the connection offers neither request nor client privacy.

    With Privacy Gateway, clients do not connect directly to the application server itself. Instead, they connect to Privacy Gateway, which in turn connects to the server. This means that the server only observes connections from Privacy Gateway, not individual connections from clients, yielding a different view:

    - ipAddress: 104.16.5.5 # a Cloudflare IP
    - ASN: 13335
    - AS Organization: Cloudflare
    - tlsCipher: ECDHE-ECDSA-AES128-GCM-SHA256 # shared across several clients
    - tlsVersion: TLSv1.3
    - Country: US
    - Region: California
    - City: Los Angeles
    

    Privacy Gateway configuration with a client anonymity set of size k

    This is request privacy. All information about the client’s location and identity are hidden from the application server. And all details about the application data are hidden from Privacy Gateway. For sensitive applications and protocols like DNS, keeping this metadata separate from the application data is an important step towards improving end-user privacy.

    Moreover, applications should take care to not reveal sensitive, per-client information in their individual requests. Privacy Gateway cannot guarantee that applications do not send identifying info – such as email addresses, full names, etc – in request bodies, since it cannot observe plaintext application data. Applications which reveal user identifying information in requests may violate client privacy, but not request privacy. This is why – unlike our full application-level Privacy Proxy product – Privacy Gateway is not meant to be used as a generic proxy-based protocol for arbitrary applications and traffic. It is meant to be a special purpose protocol for sensitive applications, including DNS (as is evidenced by Oblivious DNS-over-HTTPS), telemetry data, or generic API requests as discussed above.

    Integrating Privacy Gateway into your application

    Integrating with Privacy Gateway requires applications to implement the client and server side of the OHTTP protocol. Let’s walk through what this entails.

    Server Integration

    The server-side part of the protocol is responsible for two basic tasks:

  • Publishing a public key for request encapsulation; and
  • Decrypting encapsulated client requests, processing the resulting request, and encrypting the corresponding response.
  • A public encapsulation key, called a key configuration, consists of a key identifier (so the server can support multiple keys at once for rotation purposes), cryptographic algorithm identifiers for encryption and decryption, and a public key:

    HPKE Symmetric Algorithms {
      HPKE KDF ID (16),
      HPKE AEAD ID (16),
    }
    
    OHTTP Key Config {
      Key Identifier (8),
      HPKE KEM ID (16),
      HPKE Public Key (Npk * 8),
      HPKE Symmetric Algorithms Length (16),
      HPKE Symmetric Algorithms (32..262140),
    }
    

    Clients need this public key to create their request, and there are lots of ways to do this. Servers could fix a public key and then bake it into their application, but this would require a software update to rotate the key. Alternatively, clients could discover the public key some other way. Many discovery mechanisms exist and vary based on your threat model – see this document for more details. To start, a simple approach is to have clients fetch the public key directly from the server over some API. Below is a snippet of the API that our open source OHTTP server provides:

    func (s *GatewayResource) configHandler(w http.ResponseWriter, r *http.Request) {
    	config, err := s.Gateway.Config(s.keyID)
    	if err != nil {
    		http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
    		return
    	}
    	w.Write(config.Marshal())
    }
    

    Once public key generation and distribution is solved, the server then needs to handle encapsulated requests from clients. For each request, the server needs to decrypt the request, translate the plaintext to a corresponding HTTP request that can be resolved, and then encrypt the resulting response back to the client.

    Open source OHTTP libraries typically offer functions for request decryption and response encryption, whereas plaintext translation from binary HTTP to an HTTP request is handled separately. For example, our open source server delegates this translation to a different library that is specific to how Go HTTP requests are represented in memory. In particular, the function to translate from a plaintext request to a Go HTTP request is done with a function that has the following signature:

    func UnmarshalBinaryRequest(data []byte) (*http.Request, error) {
    	...
    }
    

    Conversely, translating a Go HTTP response to a plaintext binary HTTP response message is done with a function that has the following signature:

    type BinaryResponse http.Response
    
    func (r *BinaryResponse) Marshal() ([]byte, error) {
    	...
    }
    

    While there exist several open source libraries that one can use to implement OHTTP server support, we’ve packaged all of it up in our open source server implementation available here. It includes instructions for building, testing, and deploying to make it easy to get started.

    Client integration

    Naturally, the client-side behavior of OHTTP mirrors that of the server. In particular, the client must:

  • Discover or obtain the server public key; and
  • Encode and encrypt HTTP requests, send them to Privacy Gateway, and decrypt and decode the HTTP responses.
  • Discovery of the server public key depends on the server’s chosen deployment model. For example, if the public key is available over an API, clients can simply fetch it directly:

    $ curl https://server.example/ohttp-configs > config.bin
    

    Encoding, encrypting, decrypting, and decoding are again best handled by OHTTP libraries when available. With these functions available, building client support is rather straightforward. A trivial example Go client using the library functions linked above is as follows:

    configEnc := ... // encoded public key
    config, err := ohttp.UnmarshalPublicConfig(configEnc)
    if err != nil {
    	return err
    }
    
    request, err := http.NewRequest(http.MethodGet, "https://test.example/index.html", nil)
    if err != nil {
    	return err
    }
    
    binaryRequest := ohttp.BinaryRequest(*request)
    encodedRequest, err := binaryRequest.Marshal()
    if err != nil {
    	return err
    }
    
    ohttpClient := ohttp.NewDefaultClient(config)
    encapsulatedReq, reqContext, err := ohttpClient.EncapsulateRequest(encodedRequest)
    
    relayRequest, err := http.NewRequest(http.MethodPost, "https://relay.example", bytes.NewReader(encapsulatedReq.Marshal()))
    if err != nil {
    	return err
    }
    relayRequest.Header.Set("Content-Type", "message/ohttp-req")
    
    client := http.Client{}
    relayResponse, err := client.Do(relayRequest)
    if err != nil {
    	return err
    }
    bodyBytes, err := ioutil.ReadAll(relayResponse.Body)
    if err != nil {
    	return err
    }
    encapsulatedResp, err := ohttp.UnmarshalEncapsulatedResponse(bodyBytes)
    if err != nil {
    	return err
    }
    
    receivedResp, err := reqContext.DecapsulateResponse(encapsulatedResp)
    if err != nil {
    	return err
    }
    
    response, err := ohttp.UnmarshalBinaryResponse(receivedResp)
    if err != nil {
    	return err
    }
    
    fmt.Println(response)
    

    A standalone client like this isn’t likely very useful to you if you have an existing application. To help integration into your existing application, we created a sample OHTTP client library that’s compatible with iOS and macOS applications. Additionally, if there’s language or platform support you would like to see to help ease integration on either or the client or server side, please let us know!

    Interested?

    Privacy Gateway is currently in early access – available to select privacy-oriented companies and partners. If you’re interested, please get in touch.

    Source:: CloudFlare