Security best practices
[TODO i18n — see issue #76] "Best practices for using AACsearch securely — protect your data, API keys, and account."
Security best practices
Security is a shared responsibility. AACsearch provides industry-grade infrastructure; you must use the account and API keys correctly.
API key management
Never commit keys to Git
CRITICAL: API keys must live in environment variables, NOT in code.
Wrong:
const apiKey = "ss_search_abc123def456"; // ❌ Don't do this!
const response = await fetch('https://app.aacsearch.com/api/v1/search', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});Right:
const apiKey = process.env.AACSEARCH_API_KEY; // ✓ Use environment variables
const response = await fetch('https://app.aacsearch.com/api/v1/search', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});Create dedicated keys per service
Instead of using a single admin key everywhere, create specialized keys:
| Key | Scope | Where to use | Step |
|---|---|---|---|
| Public search key | search | Client-side, browser, mobile app | 1 |
| Ingest key | ingest | Server-side, when uploading documents | 2 |
| Admin key | admin | Only on your personal machine, never on a server | 3 |
How to create specialized keys
- Open Search → API keys
- Click Create key
- Give it a name (e.g., "Frontend Search" or "Import Worker")
- Pick the scope: Search / Ingest / Admin
- Pick the indexes the key may access (optional)
- Click Create
- Copy the key once and save it to
.env.local
Note: the key is shown only once at creation. If you lose it, create a new one.
Key rotation
Every 3 months create new keys and delete the old ones:
# Create a new key
# (in AACsearch dashboard → API keys → Create key)
# Update environment variables
echo "AACSEARCH_API_KEY=ss_search_new_key_here" >> .env.local
# Test that the new key works
curl -H "Authorization: Bearer ss_search_new_key_here" \
https://app.aacsearch.com/api/v1/indexes
# Delete the old key
# (dashboard → API keys → Old key → Delete)If a key is compromised
If you accidentally pushed a key to a public repo or shared it with someone:
- Delete the key immediately — open API keys → Old key → Delete
- Create a new key — follow the steps above
- Audit access logs — open Settings → Audit and check for unauthorized access
- Reindex sensitive data — if it was an admin key, recreate the indexes
Account protection
Use a strong password
Use a password that:
- ✓ Includes uppercase (A–Z), lowercase (a–z), digits (0–9), and specials (!@#$%^&*)
- ✓ Is at least 12 characters long (16+ preferred)
- ✓ Doesn't match your name, email, or company name
- ✓ Has not been used on other sites
Example of a strong password:
Tr0p1cal!Sunset#2025$AACsearchBad password:
aacsearch123 ❌ Too simple
Password! ❌ Too commonUse a password manager (1Password, Bitwarden, Dashlane) to generate and store passwords.
Enable two-factor authentication (2FA)
2FA makes signing in impossible even if someone learns your password.
- Open Settings → Security
- Find the Two-factor authentication section
- Click Enable 2FA
- Pick a method:
- Authenticator app (recommended) — Google Authenticator, Microsoft Authenticator, Authy
- SMS (less secure) — code via SMS
- Save the recovery codes in a safe place
Where to keep recovery codes:
- ✓ In a password manager
- ✓ In an encrypted file on your computer
- ✓ Printed on paper in a safe
- ✗ In plain text in the cloud (Google Drive, Dropbox)
Review active sessions
If you notice suspicious activity:
- Open Settings → Security → Active sessions
- Review the list of devices you signed in from
- Click Sign out next to any unknown device
Data protection
Limit team access
-
Open Settings → Organization members
-
Create members with different rights:
- Admin — full access (owner only)
- Editor — can create and edit indexes
- Viewer — read-only access to stats and search results
- Support — can answer questions but not edit data
-
Remove members who no longer work for the company
Use HTTPS everywhere
Make sure all API requests go through HTTPS, not HTTP:
# ✓ Right
curl https://app.aacsearch.com/api/v1/indexes
# ✗ Wrong
curl http://app.aacsearch.com/api/v1/indexes # ❌ Data is unencryptedLog all index actions
If you need an audit of who did what:
- Open Settings → Audit
- You see every action: who created an index, who uploaded documents, who changed settings
- Export the logs for analysis (if required by law)
Backend security
Never search directly from the client
Bad:
// On the client (browser)
const apiKey = "ss_search_..."; // ❌ API key visible in source code
const response = await fetch('https://app.aacsearch.com/api/v1/search', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});Good:
// On the client (browser)
const response = await fetch('https://your-domain.com/api/search', {
method: 'POST',
body: JSON.stringify({ query: 'sneakers' })
});
// On the server (Node.js, Python, Go)
const apiKey = process.env.AACSEARCH_API_KEY; // Key in environment variables
const searchResponse = await fetch('https://app.aacsearch.com/api/v1/search', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});Use environment variables
Right:
# .env.local (do not commit to Git!)
AACSEARCH_API_KEY=ss_search_abc123def456
AACSEARCH_ADMIN_KEY=ss_admin_xyz789uvw012// code.js
const apiKey = process.env.AACSEARCH_API_KEY;In GitHub Actions / CI-CD:
# .github/workflows/deploy.yml
- name: Deploy
env:
AACSEARCH_API_KEY: ${{ secrets.AACSEARCH_API_KEY }}
run: npm startAdd server-side rate limiting
If you expose a public search endpoint, protect it from DDoS:
// Express.js with express-rate-limit
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // up to 100 requests per 15 minutes
});
app.post('/api/search', limiter, async (req, res) => {
// Your search code
});# Django with django-ratelimit
from django_ratelimit.decorators import ratelimit
@ratelimit(key='ip', rate='100/15m') # 100 requests per 15 minutes
def search(request):
# Your search codeValidate user input
Never pass user input straight to the AACsearch API:
// ❌ Bad
app.post('/search', (req, res) => {
const query = req.query.q; // Dangerous!
fetch(`https://app.aacsearch.com/api/v1/search?query=${query}`, ...);
});
// ✓ Good
const Joi = require('joi');
app.post('/search', (req, res) => {
const { error, value } = Joi.object({
query: Joi.string().max(500).required(),
limit: Joi.number().max(100).default(20)
}).validate(req.query);
if (error) {
return res.status(400).json({ error: error.message });
}
fetch(`https://app.aacsearch.com/api/v1/search`, {
method: 'POST',
body: JSON.stringify({ query: value.query, limit: value.limit })
});
});Frontend security
Use public search keys (read-only)
When embedding the widget on a site, use a public search key that can only read data:
<div id="aac-search"></div>
<script
src="https://app.aacsearch.com/api/widget/widget.js"
data-api-key="ss_search_..." <!-- Public key, safe in the browser -->
data-index-slug="products"
data-container="#aac-search">
</script>A public search key can:
- ✓ Search the index
- ✓ Receive results
A public search key CANNOT:
- ✗ Upload documents
- ✗ Delete documents
- ✗ Change configuration
- ✗ See other API keys
Use Content Security Policy (CSP)
Protect against XSS and script injection:
<!-- In <head>, NOT inside the widget -->
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://app.aacsearch.com; img-src 'self' data: https:;">This allows scripts only from your domain and aacsearch.com.
Monitor browser requests
Use Developer Tools (F12) to inspect requests:
- Open F12 → Network
- Make a search in the widget
- Look at the
/api/v1/searchrequest - Verify:
- ✓ Uses HTTPS (not HTTP)
- ✓ Header
Authorization: Bearer ss_search_... - ✓ Sends only the necessary data (query, filters)
- ✗ Does NOT send personal user data (IP, email, phone)
Incident response
If you spot unauthorized access
- Immediately delete the compromised key — open API keys → Delete
- Check logs — open Settings → Audit and look at the date and time of the access
- See what was downloaded — which indexes and documents were touched
- Contact support — for serious breaches reach out to security@aacsearch.com
If you lost the password
- On the sign-in page click Forgot password?
- Enter your email
- Click the link in the email (valid for 24 hours)
- Set a new password
- Sign in with the new password
If the email didn't arrive:
- Check the Spam folder
- Wait 5 minutes
- Email support@aacsearch.com
If you lost the 2FA key
If you no longer have access to the authenticator app:
- On the sign-in page click Lost access to 2FA?
- Use one of the recovery codes you saved when enabling 2FA
- Set up a new 2FA app
- If you don't have recovery codes, email support@aacsearch.com
Standards compliance
SOC 2 Type II
We have completed a SOC 2 Type II audit, which checks:
- ✓ Security — protection from unauthorized access
- ✓ Availability — service is available 99.9% of the time
- ✓ Processing Integrity — data is not lost or corrupted
- ✓ Confidentiality — data stays confidential
- ✓ Privacy — compliance with data protection laws
Security checklist
Before launching in production verify:
- API keys are stored in
.env.local, not in code - 2FA is enabled on the account
- Different keys for different services (search, ingest, admin)
- HTTPS everywhere (not HTTP)
- Rate limiting on the server
- Input validation
- Audit logs for critical operations
- Team members have only the rights they need
Help and support
If you have security questions:
- Email: security@aacsearch.com
- Bug bounty: Submit a report
- Don't disclose vulnerabilities publicly — contact us first
Frequently asked questions
Q: Do I need 2FA? A: It is recommended, especially if you have admin access or store sensitive data.
Q: How often should I change the password? A: If it is strong and unique, you don't need to. Only after suspicious activity or compromise.
Q: Can I store the API key in a .env file in Git?
A: No. Add .env.local to .gitignore and keep keys only locally or in CI/CD secrets.
Q: What if I accidentally pushed a key to a public repo?
A: Delete the key from AACsearch immediately, create a new one, and clean up Git history (use git filter-branch or BFG Repo-Cleaner).