Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trust-Signal: A New HTTP Header for Verifying Server Authenticity through Cryptographic Signatures #466

Open
kkshitish9 opened this issue Jan 22, 2025 · 4 comments
Labels

Comments

@kkshitish9
Copy link

Proposal for HTTP Trust-Signal Header

Title:

Trust-Signal: A New HTTP Header for Verifying Server Authenticity through Cryptographic Signatures


Abstract:

The Trust-Signal HTTP header introduces a mechanism for verifying server authenticity by allowing web servers to sign their responses with a cryptographic signature. This header is designed to provide an additional layer of security, ensuring that responses come from trusted sources, and mitigating risks of man-in-the-middle attacks, phishing, and server impersonation. It provides a verifiable way for clients to confirm the integrity and origin of the data they receive.


Problem Statement:

Web security mechanisms, such as HTTPS (TLS/SSL) and certificates, are widely deployed but do not provide fine-grained verification for every response from the server. In some scenarios, users or applications need to verify the authenticity of the data they receive on a per-response basis, beyond the generic connection encryption and server certification.

For example:

  • Phishing: Malicious actors impersonate trusted services to provide fraudulent responses.
  • Man-in-the-Middle Attacks (MITM): Attackers intercept and manipulate responses, altering the content before it reaches the client.
  • Server Impersonation: A server might be compromised or faked, serving content that seems legitimate but is not.

Proposed Solution:

The Trust-Signal HTTP header would provide a cryptographic signature for each HTTP response, ensuring the client can verify the server’s identity and data integrity for each transaction.


Trust-Signal Header Format:

Trust-Signal: verified; sig=<signature>

Where:

  • verified: Denotes that the server’s response has been verified with a cryptographic signature.
  • sig=: A cryptographic signature that corresponds to the response’s content. The signature is typically generated using a private key and can be verified by the client using the server’s public key.

How It Works:

  1. Server-Side:

    • The server signs the response body (or selected parts of it) using a private key.
    • The server generates the Trust-Signal header, appending the cryptographic signature.
  2. Client-Side:

    • The client verifies the signature using the server’s public key, which could be obtained from a trusted source (e.g., a server certificate, a dedicated public key registry, or a predefined list of trusted keys).
    • If the signature matches, the client trusts the response. If it does not, the client can reject the response or alert the user.

Use Cases:

  1. Anti-Phishing:
    Clients can verify that the content they receive comes from the expected trusted source and not a fraudulent impersonator.

  2. Man-in-the-Middle Protection:
    Even if an attacker intercepts and modifies the response, the altered response will fail the cryptographic verification, protecting the integrity of the data.

  3. Sensitive Transactions:
    Applications requiring high security, such as banking or e-commerce, could use the Trust-Signal header to ensure that every critical action, like making payments or transferring sensitive data, is trusted and authenticated.


Security Considerations:

  • Private/Public Key Management: The security of this mechanism depends on proper management and protection of the private key used for signing. Public keys should be securely distributed and accessible by clients.
  • Response Integrity: The signature should cover the entire response body to prevent tampering. Partial response signing (e.g., only headers) should be avoided.
  • Non-repudiation: Using cryptographic signatures provides non-repudiation, which ensures that the server cannot deny the authenticity of the signed response.

Example Implementation:

  1. Server-Side (example in Python):
import hashlib
import base64
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

def sign_response(content, private_key):
    # Hash the content
    digest = hashlib.sha256(content.encode()).digest()
    
    # Sign the hash with the private key
    signature = private_key.sign(
        digest,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    
    # Base64 encode the signature
    encoded_signature = base64.b64encode(signature).decode()
    return f"Trust-Signal: verified; sig={encoded_signature}"

# Example response content and private key (in practice, load from secure storage)
response_content = "User data"
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)

# Sign the response content
header_value = sign_response(response_content, private_key)
print(header_value)
  1. Client-Side (Example in Python):
import base64
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding

def verify_signature(response_content, signature, public_key):
    digest = hashlib.sha256(response_content.encode()).digest()
    
    # Decode the base64 signature
    decoded_signature = base64.b64decode(signature)
    
    # Verify the signature using the public key
    public_key.verify(
        decoded_signature,
        digest,
        padding.PKCS1v15(),
        hashes.SHA256()
    )
    return True

# Example response content and public key (in practice, load from trusted source)
response_content = "User data"
public_key = rsa.generate_private_key(public_exponent=65537, key_size=2048).public_key()

# Simulate the signature and verification process
signature = "<signature from server>"
verify_signature(response_content, signature, public_key)

Benefits:

  • Increased Security: Adds a layer of security to HTTP responses, reducing the risk of impersonation and man-in-the-middle attacks.
  • Trust Establishment: Allows clients to independently verify server authenticity for each response, beyond just a secure connection.
  • Granular Control: Provides per-response verification, useful for sensitive content or transactions.


Conclusion:

The Trust-Signal header offers a novel way to enhance HTTP security by providing per-response cryptographic authentication. This proposal aims to give users and applications more control over the trustworthiness of responses, addressing vulnerabilities that may not be fully covered by existing mechanisms.

Let me know if you have any questions

@HanadaLee
Copy link

HanadaLee commented Jan 22, 2025

Do you want to calculate the hash and signature of the response body?

If the response is provided directly from the local disk, it is possible to calculate the hash and signature. However, it is obviously against the principle of performance priority to calculate the same content repeatedly. Therefore, even if it is implemented, the hash and signature values ​​must be prepared in advance and stored together with the original file, just like the gzip_static module. The gzip_static module will distribute filename.sign to the client, and this hypothetical module should only put the value of filename.hash in the Trust-Signal header.

If the response comes from upstream (using proxy_pass and other scenarios), this requires that the response body is fully buffered, that is, the response body must be completely pulled from the upstream, and the signature must be calculated before the response header and response body can be provided to the downstream. This scenario will have a very serious performance degradation for the distribution of larger files. In this case, the signature should be provided by the upstream server instead of nginx.

@kkshitish9
Copy link
Author

Thank you for your detailed feedback and insights regarding the Trust-Signal proposal.

  1. Hash and Signature for Local Files:
    I agree that repeatedly calculating the hash and signature for static files stored locally can negatively impact performance. To address this, I propose the following:

    • Implement a hash_static module (similar to gzip_static) that generates and stores hash/signature values for static files at deployment time.
    • These precomputed values can then be appended to the Trust-Signal header dynamically during response handling, eliminating runtime overhead.
  2. Response from Upstream Servers:

    • For scenarios involving upstream servers (e.g., proxy_pass), I agree that buffering and calculating the signature at the Nginx level would lead to significant performance degradation.
    • As a solution, I suggest enforcing a requirement for upstream servers to provide precomputed hash/signature values in their responses (via headers or metadata). Nginx could then validate and forward these values downstream.
    • To facilitate backward compatibility and adoption, Nginx could optionally include a directive (e.g., trust_signal_enable) that would only activate this feature for upstreams explicitly configured to support it.
  3. Handling Large Files:

    • For distributing large files, leveraging existing Nginx caching mechanisms (e.g., proxy_cache) can mitigate performance hits by storing hash/signature values alongside cached responses.
    • This approach ensures minimal repeated computation while still delivering the integrity assurance provided by the Trust-Signal header.

Additional Considerations:

To ensure this feature aligns with Nginx's principles and user needs, I am happy to:

  • Conduct benchmarks to measure the performance impact of proposed implementations.
  • Provide a proof-of-concept module demonstrating the precomputation and header inclusion workflows.
  • Collaborate with the community to refine and optimize the feature.

I strongly believe that the Trust-Signal feature can enhance response integrity and security in modern web applications without compromising Nginx’s renowned performance standards.

Thank you

@kkshitish9
Copy link
Author

  1. Hash and Signature for Local Disk Resources
  2. Handling Upstream Responses with Precomputed Hash

1. Case: Hash and Signature for Local Disk Resources

Solution Approach

  • Precompute hash values of static files and store them alongside the files.
  • Use NGINX’s gzip_static module-like approach to serve precomputed .hash files with a custom Trust-Signal header.

Implementation Steps

(a) Precompute Hash Values

Use a script to calculate and store hash values for static files in the directory.

#!/bin/bash
STATIC_DIR="/var/www/html/static"

for file in "$STATIC_DIR"/*; do
    if [[ -f "$file" ]]; then
        sha256sum "$file" | awk '{print $1}' > "$file.hash"
    fi
done
(b) Modify NGINX Configuration

Instruct NGINX to append the precomputed hash value to the Trust-Signal header.

http {
    server {
        listen 80;
        server_name example.com;

        location /static/ {
            root /var/www/html;

            header_filter_by_lua_block {
                local filepath = ngx.var.request_filename .. ".hash"
                local file = io.open(filepath, "r")
                if file then
                    local hash = file:read("*a")
                    file:close()
                    if hash then
                        ngx.header["Trust-Signal"] = hash:gsub("%s+", "")
                    end
                end
            }
        }
    }
}
(c) Reload NGINX
sudo nginx -t && sudo systemctl reload nginx
(d) Test the Response
curl -I http://example.com/static/sample.txt

Expected Output:

HTTP/1.1 200 OK
Trust-Signal: <precomputed-hash-value>
Content-Type: text/plain

2. Case: Handling Upstream Responses

Solution Approach

  • Require the upstream server to precompute the hash value and include it in a custom header (e.g., X-Upstream-Hash).
  • Configure NGINX to intercept and transform this header into the Trust-Signal header.

Implementation Steps

(a) Upstream Server Configuration

The upstream server (e.g., another NGINX instance or application server) must calculate the hash of the response body and include it in the response header.

Example Code (Python Flask as upstream server):

from flask import Flask, request, Response
import hashlib

app = Flask(__name__)

@app.route('/api/data', methods=['GET'])
def serve_data():
    data = "This is the response body."
    hash_value = hashlib.sha256(data.encode()).hexdigest()
    response = Response(data, status=200, mimetype='text/plain')
    response.headers["X-Upstream-Hash"] = hash_value
    return response

if __name__ == '__main__':
    app.run(port=8081)
(b) NGINX Proxy Configuration

Modify NGINX to forward requests to the upstream server and include the upstream hash as the Trust-Signal header.

http {
    server {
        listen 80;
        server_name example.com;

        location /proxy/ {
            proxy_pass http://localhost:8081/api/data;

            header_filter_by_lua_block {
                local upstream_hash = ngx.header["X-Upstream-Hash"]
                if upstream_hash then
                    ngx.header["Trust-Signal"] = upstream_hash
                end
            }
        }
    }
}
(c) Test the Response
curl -I http://example.com/proxy/

Expected Output:

HTTP/1.1 200 OK
Trust-Signal: <hash-from-upstream>
Content-Type: text/plain

Performance Considerations

  1. Local Disk Case
  • Precomputing and storing hash values prevents recalculation, ensuring optimal performance.
  1. Upstream Case
  • The upstream server calculates the hash to avoid buffering the response body in NGINX.
  • The upstream server’s hash is propagated downstream efficiently via headers.

Please Let me know if you have any further questions. This is a small demo of your questions with possible practical solutions

Thank you

@kkshitish9
Copy link
Author

My idea is new and I want to mainstream this. To get support of industry. That's why i put my proposal here. I think it will work with nginx. Because it will give secure way to explore internet through nginx. Trust-Signal is very much in developing stage. Looking to support for my this from open community.

Thank you and God Bless You all 😊

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants