AI Security

Protecting Customer Documents in an OpenAI‑Powered Summarization Pipeline

TL;DR: Store source files in encrypted, access‑controlled storage (e.g., AWS S3 with bucket policies), send only the minimal excerpt to OpenAI via function calling, protect the API key with least‑privilege scopes, log every request, and delete the temporary file after the summary is generated. This keeps customer data confidential while still gaining the benefits of AI summarization.

What is the threat model for an AI summarization pipeline?

When you feed a customer document to an LLM, the data leaves your environment and is processed by a third‑party service. The main risks are:

Mitigating these risks starts with a clear data‑flow diagram and a set of concrete controls.

How should source files be stored before they reach the model?

Use a cloud object store that supports server‑side encryption (SSE) and fine‑grained IAM policies. For example, an S3 bucket with the following settings:

{
  "Bucket": "customer‑docs",
  "ServerSideEncryptionConfiguration": {
    "Rule": {"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "AES256"}}
  },
  "Policy": "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Deny\",\"Principal\":\"*\",\"Action\":\"s3:GetObject\",\"Resource\":\"arn:aws:s3:::customer‑docs/*\",\"Condition\":{\"Bool\":{\"aws:SecureTransport\":false}}}] }"
}

Only the service account that runs the summarization job receives s3:GetObject permission. No public read access is allowed.

How can I limit the amount of data sent to OpenAI?

OpenAI’s Agents documentation describes function calling. Instead of sending the whole file, extract a short excerpt (e.g., the first 2 KB or a relevant paragraph) in your code, then call a custom function like summarize_excerpt. The model receives only the excerpt plus a concise instruction, dramatically reducing exposure.

def summarize_excerpt(text: str) -> str:
    response = openai.ChatCompletion.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": f"Summarize the following text in 3 sentences:\n\n{text}"}],
        temperature=0.0,
    )
    return response.choices[0].message.content

Because the function is defined in your code, you control the prompt and can enforce a maximum token limit.

How should API keys and service tokens be scoped?

OpenAI currently issues a single secret key per account. To apply least‑privilege principles, create a separate OpenAI organization for each production environment and generate a distinct key for each. Store the keys in a secret manager (e.g., AWS Secrets Manager) and rotate them at least every 90 days.

When using a serverless function (e.g., Cloudflare Workers AI), bind the secret at the edge runtime so it never appears in source code.

What logging and audit steps are required?

Every request that touches a customer document must be logged with the following fields:

  1. Timestamp (UTC)
  2. Request ID (UUID)
  3. User or service account that triggered the job
  4. Document identifier (hashed, not the raw name)
  5. Number of tokens sent to OpenAI
  6. Response status (success, error, rate‑limit)

Send these logs to a centralized SIEM or a simple CloudWatch Log Group. Retain logs for at least 90 days to satisfy most privacy regulations.

Should I add a human‑in‑the‑loop checkpoint?

For high‑risk documents (e.g., contracts, medical records), insert a short approval step before the excerpt is sent. A lightweight UI can display the excerpt and ask the operator to confirm “Send to AI?” This keeps the workflow fast while ensuring a human reviews the exact data that will leave your environment.

How do I clean up temporary data?

After the summary is stored (e.g., in a separate encrypted bucket), delete the original excerpt file immediately. Use an automated lifecycle rule or a short‑lived pre‑signed URL that expires after 5 minutes.

Putting it all together – a minimal workflow

# 1. Retrieve the document (encrypted S3)
obj = s3.get_object(Bucket='customer-docs', Key=doc_key)
raw = obj['Body'].read().decode('utf-8')

# 2. Extract a safe excerpt (max 2 KB)
excerpt = raw[:2048]

# 3. Optional human approval (skip for low‑risk docs)
if requires_approval:
    approved = request_human_approval(excerpt)
    if not approved:
        raise Exception('User denied AI processing')

# 4. Call OpenAI via function calling
summary = summarize_excerpt(excerpt)

# 5. Store the summary in a separate encrypted bucket
s3.put_object(Bucket='summaries', Key=summary_key, Body=summary.encode('utf-8'))

# 6. Log the transaction
log_event({
    'request_id': uuid4(),
    'doc_hash': sha256(doc_key.encode()).hexdigest(),
    'tokens_sent': len(excerpt.split()),
    'status': 'success'
})

# 7. Delete the temporary excerpt
s3.delete_object(Bucket='temp-excerpts', Key=excerpt_key)

Following these steps gives you a defensible security posture while still leveraging OpenAI’s powerful summarization capabilities.

For hands‑on assistance, consider a security review with AISecAll.

Need a practical AI security review?

AISecAll reviews prompts, tool permissions, document flows, and agent behavior so small teams can use AI without guessing where the risk sits.

Book a call Discuss a project