5.1 KiB
5.1 KiB
Provider Security Fixes - Migrations 00059 & 00060
Overview
Two critical security vulnerabilities in provider-related functions have been fixed to prevent privilege escalation and unauthorized credit manipulation.
Security Fix 1: register_provider Authorization Check (Migration 00059)
Vulnerability
The register_provider function accepted a p_user_id parameter without verifying that the caller was acting on their own account. This allowed any authenticated user to potentially register as a provider on behalf of another user.
Fix Applied
Added authorization check at the beginning of the function:
-- SECURITY: caller must be acting on their own profile only
IF p_user_id IS DISTINCT FROM auth.uid() THEN
RAISE EXCEPTION 'Unauthorized: cannot register provider on behalf of another user';
END IF;
Impact
- ✅ Users can only register themselves as providers
- ✅ Prevents impersonation attacks
- ✅ Maintains data integrity for provider profiles
- ✅ Protects provider_services and provider_wallets tables
Function Signature
register_provider(
p_user_id UUID,
p_business_name TEXT,
p_business_description TEXT,
p_destinations TEXT[],
p_activity_categories TEXT[]
) RETURNS JSON
Security Fix 2: add_credits Authorization (Migration 00060)
Vulnerability
The add_credits function could potentially be called by any authenticated user, allowing them to grant themselves unlimited credits without payment.
Fix Applied
- Authorization Check: Only
service_role(payment webhooks) or admin users can add credits:
-- SECURITY: Only service_role or admin may add credits
IF auth.role() != 'service_role' THEN
IF NOT EXISTS (
SELECT 1 FROM profiles
WHERE id = auth.uid() AND role = 'admin'
) THEN
RAISE EXCEPTION 'Unauthorized: only service_role or admin can add credits';
END IF;
END IF;
- Permission Revocation: Removed execute permissions from regular users:
REVOKE EXECUTE ON FUNCTION add_credits(UUID, INTEGER, TEXT) FROM PUBLIC;
REVOKE EXECUTE ON FUNCTION add_credits(UUID, INTEGER, TEXT) FROM authenticated;
GRANT EXECUTE ON FUNCTION add_credits(UUID, INTEGER, TEXT) TO service_role;
Impact
- ✅ Only payment webhooks (service_role) can add credits
- ✅ Admin users can manually add credits for support purposes
- ✅ Regular users cannot grant themselves free credits
- ✅ Protects revenue and credit economy integrity
Function Signature
add_credits(
p_provider_id UUID,
p_amount INTEGER,
p_description TEXT DEFAULT 'Credit purchase'
) RETURNS JSON
Security Architecture
register_provider Flow
- Authentication: User must be logged in (authenticated role)
- Authorization: User can only register their own account (
p_user_id = auth.uid()) - Profile Check: Verifies profile exists before proceeding
- Role Update: Changes profile role to 'provider'
- Service Record: Creates/updates provider_services entry
- Wallet Creation: Initializes provider wallet with 0 credits
add_credits Flow
- Authentication: Caller must be service_role or admin
- Validation: Amount must be positive
- Wallet Creation: Creates wallet if it doesn't exist
- Row Locking: Uses
FOR UPDATEto prevent race conditions - Credit Addition: Atomically updates balance
- Transaction Record: Logs the credit addition
Testing Recommendations
Test register_provider
- ✅ User can register themselves as provider
- ✅ User cannot register another user as provider
- ✅ Anonymous users cannot call the function
- ✅ Profile must exist before registration
- ✅ Wallet is created with 0 credits
Test add_credits
- ✅ Service role can add credits (payment webhook simulation)
- ✅ Admin users can add credits
- ✅ Regular authenticated users cannot add credits
- ✅ Anonymous users cannot add credits
- ✅ Negative amounts are rejected
- ✅ Transaction is recorded correctly
Related Files
supabase/migrations/00059_fix_register_provider_auth_check.sqlsupabase/migrations/00060_secure_add_credits_function.sqlsupabase/migrations/00013_add_provider_registration_function.sql(original)supabase/migrations/00010_create_purchase_lead_function.sql(original add_credits)
Deployment Status
- ✅ Migration files created
- ✅ Migrations applied to database
- ✅ Functions verified as SECURITY DEFINER
- ✅ Permissions configured correctly
Security Checklist
- Authorization checks in place
- Input validation implemented
- Row-level locking for concurrency
- Proper error messages (no information leakage)
- Permissions restricted appropriately
- Audit trail (transaction records)
- SECURITY DEFINER used correctly
- search_path set explicitly
Additional Security Measures
Both functions use:
SECURITY DEFINER: Runs with function owner's privilegesSET search_path = public: Prevents schema injection attacks- Explicit authorization checks before any data modification
- Atomic transactions with proper error handling
- Row locking to prevent race conditions