Security
Security
Identity, Claims, and Roles
• Identity: This represents who the user is, typically verified through an authentication provider and represented by a unique user ID.
• Claims: These are key-value pairs embedded in a token that assert facts about the user, such as their email, tenant_id, or organization_id.
• Roles: Roles group permissions together (e.g., Admin, Viewer, BillingManager) to determine what broad actions an identity is allowed to perform.
JWT and OAuth 2.0
• OAuth 2.0: This framework handles delegated authorization, allowing an application to obtain limited access to user accounts without exposing credentials.
• JSON Web Tokens (JWT): A JWT is a compact, cryptographically signed token used to securely transmit claims between parties.
• Token Verification: Your server must validate the JWT signature, expiration (exp), issuer (iss), and audience (aud) on every incoming request before reading any claims.
Server-Side Authorization
• Zero Trust on Client Inputs: Never rely on a customer_id or tenant_id sent in a URL path, query string, or JSON body to determine data ownership.
• Context Extraction: Extract the trusted tenant_id and roles directly from the validated JWT claims or a server-side session store.
• Middleware Enforcement: Use centralized authorization middleware or policies to verify that the user's role permits the requested action (e.g., checking if a user has pricing:read before executing the controller).
Data-Level Access Control
• Tenant Scoping in Queries: Every database query must include a mandatory WHERE tenant_id = :authenticated_tenant_id clause.
• Row-Level Security (RLS): Databases like PostgreSQL support native RLS policies that automatically filter rows based on the current session's tenant context, acting as a safety net against developer error.
• Indirect Object References: Avoid exposing sequential database IDs (like /invoices/1001) to prevent Insecure Direct Object Reference (IDOR) attacks. Use UUIDs and still enforce ownership checks on every lookup.
Preventing Cross-Customer Data & Pricing Leaks
• Isolate Pricing Tables: Store custom customer pricing in a table explicitly keyed by tenant_id and product_id. Never return a global pricing catalog with customer-specific overrides visible in the payload.
• Server-Side Price Calculation: Always calculate quotes, discounts, and totals on the backend using the authenticated tenant_id. Ignore any price or discount values sent from the frontend.
• Cache Key Isolation: If you cache API responses or pricing tiers (e.g., in Redis), always include the tenant_id in the cache key (e.g., pricing:{tenant_id}:{product_id}) so cached data never bleeds across customers.
• Automated Security Testing: Write integration tests that authenticate as Customer A and explicitly attempt to read, update, or fetch pricing for Customer B's IDs, asserting that the server returns a 403 Forbidden or 404 Not Found.
Comments
Post a Comment