This chapter is primary reading for auditors, security researchers, and CISOs. We use STRIDE threat modeling to inventory PIF AI’s attack surface, provide a mitigation for each threat, and map to OWASP Top 10 (2021). The chapter closes with the specific security rationale behind the 5-locale i18n + super-admin exception.
STRIDE1 (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege) is the threat taxonomy PIF AI adopts.
Assets prioritized for protection:
| Asset | Sensitivity | Consequence |
|---|---|---|
| Formulation (INCI + concentrations) | Critical | Trade-secret leak → legal liability |
| SA signature | Critical | Forgery → regulatory liability + criminal forgery |
| User credentials | High | Identity theft → broad data exposure |
| Toxicology summaries | Medium | Derived from public data; limited impact |
| System configuration | High | Impacts all tenants’ availability |
| Threat | Example attack | Mitigation |
|---|---|---|
| Spoofing | Impersonate another user | JWT short-lived + refresh + device fingerprint (planned) |
| Tampering | Modify requests / audit | HTTPS everywhere + audit log immutable |
| Repudiation | SA denies signature | SA requires TOTP + e-signature recorded with IP + UA in audit |
| Information disclosure | Formulation leak | Four-layer isolation (§10) + AES-256 + alerts on anomaly |
| Denial of service | Flood AI calls | Rate limit + Cloudflare + fail-soft degradation |
| Elevation of privilege | Regular user → super admin | DB CHECK on role + super-admin requires separate user record (not upgrade) |
PIF AI offers:
| Token | Lifetime | Storage | Use |
|---|---|---|---|
| Access Token | 15 min | Memory (not localStorage) | API authorization |
| Refresh Token | 7 days | HttpOnly Cookie | Access token renewal |
| Session cookie (NextAuth) | 24 hours | HttpOnly + Secure | Frontend session |
Short access tokens shrink stolen-token exposure. Refresh tokens in HttpOnly cookies are not XSS-exfiltratable.
SA is a high-sensitivity role. All sa_review.* endpoint calls require TOTP (Google Authenticator / Authy compatible):
# app/core/security.py (conceptual)
def verify_totp(user: User, code: str) -> bool:
if not user.totp_enabled:
return True # Non-SA users who haven't opted in
totp = pyotp.TOTP(user.totp_secret)
return totp.verify(code, valid_window=1)
users.totp_secret is stored with application-level encryption, not plaintext — even DB dumps don’t reveal the seed.
Super admin (Baiyuan Tech internal ops) is PIF’s highest-privilege role. Isolations:
/super-admin/loginpifai_superadmin) with BYPASSRLSmax-age=31536000; includeSubDomains; preloadAs in §8.5, formulation files use app-layer AES-256-GCM + S3 SSE-KMS:
# app/services/encryption.py (conceptual)
def encrypt_formula(plaintext: bytes, key: bytes) -> EncryptedPayload:
nonce = os.urandom(12)
aes = Cipher(algorithms.AES(key), modes.GCM(nonce)).encryptor()
ciphertext = aes.update(plaintext) + aes.finalize()
return EncryptedPayload(
nonce=nonce,
ciphertext=ciphertext,
tag=aes.tag,
)
FILE_ENCRYPTION_KEY is 256-bit, managed by Secret Manager (AWS Secrets Manager / HashiCorp Vault). Never in git, never in .env plaintext (loaded dynamically via IAM role / service token).
Sensitive fields are encrypted at application layer before DB write:
| Field | Encryption | Reason |
|---|---|---|
users.totp_secret |
Fernet (symmetric) | 2FA seed |
users.sa_certificate_url |
Fernet | SA qualification link |
organizations.api_keys |
Fernet | Third-party service keys (ECHA, etc.) |
See §8.4 for the full list. Security-focused events:
| Event | Trigger |
|---|---|
auth.login_success / auth.login_failed |
app/api/v1/auth.py |
auth.totp_failed |
TOTP verification error |
org.data_export |
Organization data download |
admin.bypass_rls |
Super admin uses BYPASSRLS |
security.suspicious_login |
Anomalous IP or excessive failures |
audit_logs to a WORM S3 bucket (Object Lock compliance mode, 10-year retention)The following trigger Slack / PagerDuty alerts:
auth.login_failed (same user or IP)admin.bypass_rls eventrag_client failures (external service outage)PIF AI supports 5 locales (zh-TW / en / ja / ko / fr) in user-facing UI. However, /super-admin/* pages deliberately do not import useI18n(). Security rationale:
This is a design decision, clearly noted in CLAUDE.md and memory memory/project_pif_i18n.md.
The 5-locale JSON is professionally translated (via Claude Code, not machine translation), but security requires:
{t("key")}, never as HTMLdangerouslySetInnerHTML on translated content| OWASP Risk | PIF AI |
|---|---|
| A01 Broken Access Control | Four-layer isolation (§10.3.3) |
| A02 Cryptographic Failures | Double AES-256 + TLS 1.3 + Fernet DB |
| A03 Injection | SQLAlchemy ORM + Pydantic + zod |
| A04 Insecure Design | Threat-model-driven design; fail-soft principle |
| A05 Security Misconfiguration | Docker image trivy scan; no DEBUG in production |
| A06 Vulnerable Components | dependabot + renovate auto-update |
| A07 ID & Auth Failures | JWT short-lived + TOTP + rate limit |
| A08 Software & Data Integrity | Immutable audit + hash chain |
| A09 Logging & Monitoring | Structured logging + real-time alerts |
| A10 SSRF | External API allowlist (PubChem/ECHA/RAG only) |
Phase 2+ plans:
| Version | Date | Summary |
|---|---|---|
| v0.1 | 2026-04-19 | First draft. STRIDE, auth, encryption, audit, i18n security, OWASP mapping |
© 2026 Baiyuan Tech. Licensed under CC BY-NC 4.0.
Nav ← Chapter 10: Central RAG · Chapter 12: Roadmap →
Microsoft. The STRIDE Threat Model. https://learn.microsoft.com/en-us/azure/security/develop/threat-modeling-tool-threats ↩