DEV Community

ZNY
ZNY

Posted on

API Security Best Practices for AI Applications in 2026

AI applications face unique security challenges. Beyond traditional API vulnerabilities, AI APIs expose new attack surfaces: prompt injection, data leakage, and model manipulation. Here's how to secure your AI-powered systems.

The AI Security Landscape

AI APIs introduce attack vectors traditional APIs don't have:

  1. Prompt injection — Malicious input that manipulates AI behavior
  2. Data exfiltration — AI accidentally leaking sensitive context
  3. Token exhaustion — attackers exhausting your quota
  4. Model extraction — Repeated queries to reverse-engineer the model
  5. Context poisoning — Injecting malicious context into conversations

Input Validation and Sanitization

`python
import re
from typing import Optional

class InputSanitizer:

Block common prompt injection patterns

BLOCKED_PATTERNS = [
r'ignore\s+previous\s+instructions',
r'ignore\s+all\s+previous',
r'system\s:\s',
r'you\s+are\s+a\s+different',
r'forget\s+everything',
r'#\s*roleplay',
]

MAX_LENGTH = 10000 # Max 10k characters
MAXTOKENSESTIMATE = MAX_LENGTH // 4 # ~2500 tokens

@classmethod
def sanitize(cls, user_input: str) -> tuple[bool, Optional[str], str]:
"""
Returns: (issafe, reason, sanitizedinput)
"""

Check length

if len(userinput) > cls.MAXLENGTH:
return False, f"Input exceeds {cls.MAXLENGTH} chars", userinput[:cls.MAX_LENGTH]

Check for blocked patterns

for pattern in cls.BLOCKED_PATTERNS:
if re.search(pattern, user_input, re.IGNORECASE):
return False, "Blocked pattern detected", ""

Strip control characters

sanitized = re.sub(r'[\x00-\x08\x0b-\x0c\x0e-\x1f\x7f]', '', user_input)

return True, None, sanitized
`

Rate Limiting

`python
from fastapi import FastAPI, Request, HTTPException
from fastapi.responses import JSONResponse
import time
from collections import defaultdict

app = FastAPI()

Simple in-memory rate limiter
class RateLimiter:
def init(self, requestsperminute: int = 60):
self.requestsperminute = requestsperminute
self.requests = defaultdict(list)

def isallowed(self, clientid: str) -> bool:
now = time.time()
minute_ago = now - 60

Clean old entries

self.requests[client_id] = [
t for t in self.requests[clientid] if t > minuteago
]

if len(self.requests[clientid]) >= self.requestsper_minute:
return False

self.requests[client_id].append(now)
return True

ratelimiter = RateLimiter(requestsper_minute=60)

@app.middleware("http")
async def ratelimitmiddleware(request: Request, call_next):
client_id = request.client.host # Or use API key
if not ratelimiter.isallowed(client_id):
return JSONResponse(
status_code=429,
content={"error": "Rate limit exceeded"}
)
response = await call_next(request)
return response
`

API Key Security

`python
import os
import hashlib
import hmac
from typing import Optional

class APIKeyManager:
"""
Never store raw API keys. Always hash them.
"""
def init(self):
self.keystore = {} # In production, use a proper database

def createkey(self, userid: str, scopes: list[str]) -> str:
import secrets
apikey = f"ofox{secrets.token_urlsafe(32)}"
keyhash = self.hashkey(apikey)

self.keystore[key_hash] = {
"userid": userid,
"scopes": scopes,
"created": time.time()
}

Return raw key ONLY ONCE to the user

return api_key

def validatekey(self, apikey: str) -> Optional[dict]:
keyhash = self.hashkey(apikey)
return self.keystore.get(key_hash)

def hashkey(self, key: str) -> str:
return hashlib.sha256(key.encode()).hexdigest()

Usage
key_manager = APIKeyManager()
rawkey = keymanager.create_key("user123", ["chat", "embeddings"])
print(f"Save this key securely: {raw_key}") # Show once
`

Prompt Injection Defense

`python
class PromptInjectionDetector:
"""
Detect attempts to override system behavior through user input.
"""
INJECTION_SIGNALS = [
"ignore previous",
"disregard your",
"new instructions:",
"[INST]",
"<>",
"<>",
"you are now",
"pretend you are",
"forget your",
"system prompt:",
]

@classmethod
def detect(cls, user_input: str) -> bool:
lowerinput = userinput.lower()
for signal in cls.INJECTION_SIGNALS:
if signal.lower() in lower_input:
return True
return False

Usage in your endpoint
@app.post("/chat")
async def chat(request: ChatRequest):
if PromptInjectionDetector.detect(request.messages[-1].content):

Log and block

logger.warning(f"Prompt injection attempt: {request.messages[-1].content[:100]}")
raise HTTPException(status_code=400, detail="Invalid input")

Continue with normal processing

`

Data Isolation in Multi-Tenant Systems

`python
class ConversationContext:
"""
Ensure user data doesn't leak between conversations.
"""
def init(self, userid: str, apikey: str):
self.userid = userid
self.apikey = apikey
self.conversationhistory = []

def add_message(self, role: str, content: str):
self.conversationhistory.append({
"role": role,
"content": content,
"userid": self.userid # Tag with user
})

def get_messages(self) -> list[dict]:

Always filter by user_id to prevent leakage

return [
m for m in self.conversationhistory
if m["userid"] == self.userid
]

def clear_history(self):

Only clear THIS user's history

self.conversationhistory = [
m for m in self.conversationhistory
if m["userid"] != self.userid
]
`

Secure Error Handling

`python
@app.exception_handler(Exception)
async def globalexceptionhandler(request: Request, exc: Exception):

Never expose internal error details in production

logger.error(f"Error: {exc}", exc_info=True)

return JSONResponse(
status_code=500,
content={
"error": "Internal server error",

Don't include: exc.message, stack trace, API keys

}
)

For API provider errors (like ofox.ai errors)
@app.post("/chat")
async def chat(request: ChatRequest):
try:
result = await callofoxapi(request)
return result
except httpx.HTTPStatusError as e:

Log full error internally

logger.error(f"ofox API error: {e.response.status_code} {e.response.text}")

Return sanitized error to client

raise HTTPException(
status_code=502,
detail="AI service temporarily unavailable"
)
`

Environment Variables (Never Hardcode)

`bash
.env (never commit this file)
OFOXAPIKEY=your-key-here
DATABASE_URL=postgresql://...
JWT_SECRET=your-secret-here

docker-compose.yml (use secrets in production)
environment:

  • OFOXAPIKEY=${OFOXAPIKEY} `

`python
Load from environment
from dotenv import load_dotenv
load_dotenv() # In development only

apikey = os.environ.get("OFOXAPI_KEY")
if not api_key:
raise ValueError("OFOXAPIKEY not set")
`

Security Checklist

[ ] Input validation on all user-provided text
[ ] Rate limiting on all endpoints
[ ] API keys hashed (never stored raw)
[ ] Prompt injection detection
[ ] Error messages don't expose internals
[ ] Environment variables for secrets (not hardcoded)
[ ] HTTPS only in production
[ ] Logging without sensitive data
[ ] Regular dependency audits (pip audit, npm audit)

Getting Started

Build secure AI applications with ofox.ai — their API includes built-in security features and 99.9% uptime guarantee.

👉 Get started with ofox.ai

This article contains affiliate links.

Tags: security,api,ai,programming,developer
Canonical URL: https://dev.to/zny10289

Top comments (0)