Third-Party Integration Security Weakness
Description
Security vulnerabilities introduced through third-party integrations, including excessive permissions granted to vendors, lack of activity monitoring, missing security assessments, and insufficient isolation between vendor access and production data. Often involves shared credentials, overly broad API access, or direct database connections to third parties.
Illustrative Cantor Point
The Cantor Point occurs when integrating third-party services - choosing between restrictive integration that may limit functionality versus permissive access that enables full features. The decision to grant production access or share credentials creates a divergent path where vendor compromises become customer breaches.
Real-World Examples / Observed In
- DoorDash (2019): 4.9 million users affected through compromised third-party vendor [See: Cases-By-Year/2019 Data Integrity Failures.md#9]
- AMCA Healthcare (2019): 20 million patient records exposed through payment processor breach [See: Cases-By-Year/2019 Data Integrity Failures.md#7]
- Target (2013): HVAC vendor credentials led to massive retail breach
- SolarWinds (2020): Supply chain attack affecting thousands of organizations
Common Consequences & Impacts
Technical Impacts
- - Unauthorized data access
- - Lateral movement opportunities
- - Persistent backdoors
- - Compliance violations
Human/Ethical Impacts
- - Personal data exposure
- - Identity theft risks
- - Privacy violations
- - Financial losses
Business Impacts
- - Data breach liability
- - Vendor relationship damage
- - Regulatory penalties
- - Customer trust erosion
Recovery Difficulty & Escalation
ADI Principles & Axioms Violated
- Principle of Trust Insufficiency: Over-trusting vendor security
- Principle of Boundary Criticality: Vendor access crosses trust boundaries
Detection / 60-Second Audit
```sql
-- Check for vendor access tracking
SELECT
NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_name IN ('vendor_access_log', 'third_party_integrations', 'vendor_permissions')
) as missing_vendor_tracking;
-- Identify shared credentials
SELECT
rolname,
rolcanlogin,
CASE
WHEN rolname LIKE '%vendor%' OR rolname LIKE '%third%' OR rolname LIKE '%partner%'
THEN 'Potential vendor account'
ELSE 'Internal account'
END as account_type,
CASE
WHEN rolsuper THEN 'CRITICAL: Superuser access'
WHEN rolcreaterole THEN 'HIGH: Can create roles'
WHEN rolcreatedb THEN 'HIGH: Can create databases'
ELSE 'Review permissions'
END as risk_level
FROM pg_roles
WHERE rolcanlogin = true
ORDER BY
CASE
WHEN rolsuper THEN 1
WHEN rolcreaterole OR rolcreatedb THEN 2
ELSE 3
END;
-- Check for external connections
SELECT
datname,
usename,
client_addr,
state,
query_start,
CASE
WHEN client_addr !~ '^10\.|^172\.(1[6-9]|2[0-9]|3[01])\.|^192\.168\.'
AND client_addr IS NOT NULL
THEN 'External connection'
ELSE 'Internal connection'
END as connection_type
FROM pg_stat_activity
WHERE datname IS NOT NULL
ORDER BY query_start DESC;
```
```sql
-- Check for vendor access tracking
SELECT
NOT EXISTS (
SELECT 1 FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name IN ('vendor_access_log', 'third_party_integrations', 'vendor_permissions')
) as missing_vendor_tracking;
-- Identify shared credentials
SELECT
user,
host,
CASE
WHEN user LIKE '%vendor%' OR user LIKE '%third%' OR user LIKE '%partner%'
THEN 'Potential vendor account'
ELSE 'Internal account'
END as account_type,
CASE
WHEN super_priv = 'Y' THEN 'CRITICAL: Superuser access'
WHEN create_priv = 'Y' THEN 'HIGH: Can create databases'
WHEN grant_priv = 'Y' THEN 'HIGH: Can grant privileges'
ELSE 'Review permissions'
END as risk_level
FROM mysql.user
WHERE account_locked = 'N'
ORDER BY
CASE
WHEN super_priv = 'Y' THEN 1
WHEN create_priv = 'Y' OR grant_priv = 'Y' THEN 2
ELSE 3
END;
-- Check for external connections
SELECT
db,
user,
host,
command,
time,
CASE
WHEN host NOT LIKE '10.%'
AND host NOT LIKE '172.16.%'
AND host NOT LIKE '172.17.%'
AND host NOT LIKE '172.18.%'
AND host NOT LIKE '172.19.%'
AND host NOT LIKE '172.2_.%'
AND host NOT LIKE '172.30.%'
AND host NOT LIKE '172.31.%'
AND host NOT LIKE '192.168.%'
AND host NOT LIKE 'localhost%'
THEN 'External connection'
ELSE 'Internal connection'
END as connection_type
FROM information_schema.processlist
WHERE db IS NOT NULL;
```
```sql
-- Check for vendor access tracking
SELECT
CASE WHEN NOT EXISTS (
SELECT 1 FROM sys.tables
WHERE name IN ('vendor_access_log', 'third_party_integrations', 'vendor_permissions')
) THEN 1 ELSE 0 END as missing_vendor_tracking;
-- Identify shared credentials
SELECT
p.name as login_name,
p.type_desc,
CASE
WHEN p.name LIKE '%vendor%' OR p.name LIKE '%third%' OR p.name LIKE '%partner%'
THEN 'Potential vendor account'
ELSE 'Internal account'
END as account_type,
CASE
WHEN IS_SRVROLEMEMBER('sysadmin', p.name) = 1 THEN 'CRITICAL: Sysadmin access'
WHEN IS_SRVROLEMEMBER('dbcreator', p.name) = 1 THEN 'HIGH: Can create databases'
WHEN IS_SRVROLEMEMBER('securityadmin', p.name) = 1 THEN 'HIGH: Security admin'
ELSE 'Review permissions'
END as risk_level
FROM sys.server_principals p
WHERE p.type IN ('S', 'U')
AND p.is_disabled = 0
ORDER BY
CASE
WHEN IS_SRVROLEMEMBER('sysadmin', p.name) = 1 THEN 1
WHEN IS_SRVROLEMEMBER('dbcreator', p.name) = 1
OR IS_SRVROLEMEMBER('securityadmin', p.name) = 1 THEN 2
ELSE 3
END;
-- Check for external connections
SELECT
DB_NAME(s.database_id) as database_name,
s.login_name,
c.client_net_address,
s.status,
s.login_time,
CASE
WHEN c.client_net_address NOT LIKE '10.%'
AND c.client_net_address NOT LIKE '172.16.%'
AND c.client_net_address NOT LIKE '172.17.%'
AND c.client_net_address NOT LIKE '172.18.%'
AND c.client_net_address NOT LIKE '172.19.%'
AND c.client_net_address NOT LIKE '172.2_.%'
AND c.client_net_address NOT LIKE '172.30.%'
AND c.client_net_address NOT LIKE '172.31.%'
AND c.client_net_address NOT LIKE '192.168.%'
AND c.client_net_address IS NOT NULL
THEN 'External connection'
ELSE 'Internal connection'
END as connection_type
FROM sys.dm_exec_sessions s
JOIN sys.dm_exec_connections c ON s.session_id = c.session_id
WHERE s.database_id > 0;
Prevention & Mitigation Best Practices
Vendor Access Management Schema:
CREATE TABLE vendor_registry ( id SERIAL PRIMARY KEY, vendor_name VARCHAR(255) UNIQUE NOT NULL, vendor_type VARCHAR(100), risk_rating INTEGER CHECK (risk_rating BETWEEN 1 AND 10), security_assessment_date DATE, security_assessment_status VARCHAR(50), contract_start_date DATE, contract_end_date DATE, primary_contact_email VARCHAR(255), security_contact_email VARCHAR(255) ); CREATE TABLE vendor_access_grants ( id SERIAL PRIMARY KEY, vendor_id INTEGER REFERENCES vendor_registry(id), access_type VARCHAR(50) CHECK (access_type IN ('api', 'database', 'sftp', 'web_portal')), resource_name VARCHAR(255), permission_level VARCHAR(50), granted_date DATE NOT NULL, expires_date DATE NOT NULL, approved_by VARCHAR(255) NOT NULL, business_justification TEXT NOT NULL, CHECK (expires_date > granted_date) ); CREATE TABLE vendor_access_reviews ( id SERIAL PRIMARY KEY, vendor_id INTEGER REFERENCES vendor_registry(id), review_date DATE NOT NULL, reviewer VARCHAR(255) NOT NULL, access_still_needed BOOLEAN NOT NULL, findings TEXT, actions_taken TEXT );API Gateway for Vendors:
CREATE TABLE vendor_api_keys ( id SERIAL PRIMARY KEY, vendor_id INTEGER REFERENCES vendor_registry(id), api_key_hash VARCHAR(64) UNIQUE NOT NULL, created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), expires_at TIMESTAMP WITH TIME ZONE NOT NULL, last_used_at TIMESTAMP WITH TIME ZONE, allowed_endpoints TEXT[], rate_limit_per_hour INTEGER DEFAULT 1000, allowed_ip_ranges INET[], is_active BOOLEAN DEFAULT true ); CREATE TABLE vendor_api_usage ( id BIGSERIAL PRIMARY KEY, api_key_hash VARCHAR(64) REFERENCES vendor_api_keys(api_key_hash), endpoint VARCHAR(255), request_time TIMESTAMP WITH TIME ZONE DEFAULT NOW(), response_code INTEGER, data_accessed TEXT[], source_ip INET ); -- Detect suspicious vendor activity CREATE OR REPLACE FUNCTION check_vendor_anomaly( p_api_key_hash VARCHAR, p_endpoint VARCHAR, p_source_ip INET ) RETURNS BOOLEAN AS $ DECLARE v_key vendor_api_keys; v_recent_requests INTEGER; BEGIN SELECT * INTO v_key FROM vendor_api_keys WHERE api_key_hash = p_api_key_hash; -- Check IP allowlist IF v_key.allowed_ip_ranges IS NOT NULL THEN IF NOT p_source_ip <<= ANY(v_key.allowed_ip_ranges) THEN RETURN FALSE; -- Suspicious: IP not in allowlist END IF; END IF; -- Check endpoint allowlist IF v_key.allowed_endpoints IS NOT NULL THEN IF NOT p_endpoint = ANY(v_key.allowed_endpoints) THEN RETURN FALSE; -- Suspicious: Endpoint not allowed END IF; END IF; -- Check rate limit SELECT COUNT(*) INTO v_recent_requests FROM vendor_api_usage WHERE api_key_hash = p_api_key_hash AND request_time > NOW() - INTERVAL '1 hour'; IF v_recent_requests >= v_key.rate_limit_per_hour THEN RETURN FALSE; -- Suspicious: Rate limit exceeded END IF; RETURN TRUE; -- All checks passed END; $ LANGUAGE plpgsql;Data Isolation for Vendors:
-- Create isolated schemas for vendor access CREATE SCHEMA vendor_accessible; -- Create views with limited data CREATE VIEW vendor_accessible.customer_summary AS SELECT customer_id, created_date, status, -- Exclude sensitive fields like SSN, payment info COUNT(*) OVER() as total_customers FROM customers WHERE data_sharing_consent = true; -- Row-level security for vendor access ALTER TABLE vendor_accessible.customer_summary ENABLE ROW LEVEL SECURITY; CREATE POLICY vendor_data_access ON vendor_accessible.customer_summary FOR SELECT USING ( EXISTS ( SELECT 1 FROM vendor_data_permissions vdp JOIN vendor_api_keys vak ON vdp.vendor_id = vak.vendor_id WHERE vak.api_key_hash = current_setting('app.current_api_key') AND vdp.customer_segment = customer_segment ) );Vendor Activity Monitoring:
CREATE TABLE vendor_activity_alerts ( id SERIAL PRIMARY KEY, vendor_id INTEGER REFERENCES vendor_registry(id), alert_type VARCHAR(100), severity VARCHAR(20), details JSONB, detected_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), resolved_at TIMESTAMP WITH TIME ZONE, resolved_by VARCHAR(255) ); -- Real-time monitoring view CREATE VIEW vendor_security_dashboard AS SELECT v.vendor_name, COUNT(DISTINCT vau.endpoint) as endpoints_accessed, COUNT(vau.id) as total_requests_24h, MAX(vau.request_time) as last_activity, COUNT(DISTINCT vau.source_ip) as unique_ips, COALESCE( (SELECT COUNT(*) FROM vendor_activity_alerts vaa WHERE vaa.vendor_id = v.id AND vaa.resolved_at IS NULL), 0 ) as open_alerts FROM vendor_registry v LEFT JOIN vendor_api_keys vak ON v.id = vak.vendor_id LEFT JOIN vendor_api_usage vau ON vak.api_key_hash = vau.api_key_hash AND vau.request_time > NOW() - INTERVAL '24 hours' GROUP BY v.id, v.vendor_name ORDER BY total_requests_24h DESC;Additional Best Practices:
- Implement OAuth2/SAML instead of shared credentials
- Use temporary, scoped credentials (AWS STS style)
- Regular vendor security assessments
- Encrypt data before sharing with vendors
- Implement vendor access kill switches
- Separate environments for vendor testing
Real World Examples
# The vulnerability chain:
1. Payment processor (AMCA) had direct database access
2. No security monitoring on vendor connections
3. Breach went undetected for 8 months
4. Affected 20+ healthcare companies
# What AMCA had access to:
- Patient names, addresses, dates of birth
- Social Security numbers
- Medical test information
- Credit card and bank account numbers
# Timeline:
- Aug 2018: Initial compromise
- Mar 2019: Discovered by external party
- Jun 2019: Public disclosure
- Jul 2019: AMCA files for bankruptcy
# Impact:
- Quest Diagnostics: 11.9M patients
- LabCorp: 7.7M patients
- BioReference: 422K patients
- Multiple class-action lawsuits
- AMCA shut down operations
# Vulnerable architecture:
class VendorIntegration:
def __init__(self):
# Bad: Shared production credentials
self.db_conn = psycopg2.connect(
host="prod-db.doordash.com",
user="vendor_analytics",
password="shared_password_2019", # Never rotated
database="production"
)
def get_customer_data(self):
# Bad: No data filtering or masking
cursor = self.db_conn.cursor()
cursor.execute("""
SELECT * FROM customers
JOIN payment_methods ON customers.id = payment_methods.customer_id
JOIN addresses ON customers.id = addresses.customer_id
""")
return cursor.fetchall()
# Result: 4.9 million users' data exposed
# Including: names, emails, addresses, phone numbers,
# order history, partial payment card numbers
# Secure implementation:
class SecureVendorIntegration:
def __init__(self, vendor_id: str):
self.vendor_id = vendor_id
self.api_gateway = VendorAPIGateway()
async def get_analytics_data(self, date_range):
# Step 1: Authenticate with temporary token
token = await self.api_gateway.get_temp_token(
vendor_id=self.vendor_id,
scope=['analytics:read'],
duration_minutes=15
)
# Step 2: Access only allowed data through API
response = await self.api_gateway.query(
token=token,
endpoint='/v1/analytics/aggregated',
params={
'start_date': date_range.start,
'end_date': date_range.end,
'fields': ['order_count', 'total_revenue'],
'exclude_pii': True
}
)
# Step 3: Log all access
await self.log_vendor_access(
vendor_id=self.vendor_id,
data_accessed='analytics_aggregated',
record_count=response.count
)
return response
# Database-level protection:
CREATE SCHEMA vendor_analytics;
-- Only aggregated, non-PII views
CREATE VIEW vendor_analytics.daily_metrics AS
SELECT
DATE(created_at) as date,
COUNT(DISTINCT customer_id) as unique_customers,
COUNT(*) as total_orders,
AVG(order_total) as avg_order_value,
-- No names, addresses, or payment info
'REDACTED' as pii_status
FROM orders
WHERE created_at >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY DATE(created_at);
-- Row-level security
ALTER TABLE vendor_analytics.daily_metrics
ENABLE ROW LEVEL SECURITY;
CREATE POLICY vendor_date_limit
ON vendor_analytics.daily_metrics
FOR SELECT
USING (date >= CURRENT_DATE - INTERVAL '90 days');
AI Coding Guidance/Prompt
Prompt: "When integrating third-party vendors:"
Rules:
- Never grant direct database access
- Always use API gateways with rate limiting
- Require time-limited credentials
- Flag any vendor with production access
- Mandate security assessments before integration
- Require data minimization strategies
Example:
# Bad: Direct vendor database access
-- Creating vendor user with too much access
CREATE USER vendor_analytics WITH PASSWORD 'shared_password';
GRANT SELECT ON ALL TABLES IN SCHEMA public TO vendor_analytics;
-- No audit trail, no restrictions, no expiration
# Good: Secured vendor integration
-- Step 1: Create isolated vendor schema
CREATE SCHEMA vendor_analytics_data;
-- Step 2: Create limited views
CREATE VIEW vendor_analytics_data.daily_metrics AS
SELECT
date_trunc('day', created_at) as date,
COUNT(*) as transaction_count,
SUM(amount) as total_amount
FROM transactions
WHERE created_at > NOW() - INTERVAL '90 days'
GROUP BY date_trunc('day', created_at);
-- Step 3: API-based access with monitoring
CREATE TABLE vendor_api_access (
access_id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
vendor_name VARCHAR(255) NOT NULL,
api_key_hash VARCHAR(64) NOT NULL,
valid_from TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
valid_until TIMESTAMP WITH TIME ZONE NOT NULL,
allowed_operations TEXT[] DEFAULT ARRAY['read'],
rate_limit_per_hour INTEGER DEFAULT 100,
last_access TIMESTAMP WITH TIME ZONE,
total_requests INTEGER DEFAULT 0
);
-- Step 4: Automated monitoring
CREATE OR REPLACE FUNCTION log_vendor_access()
RETURNS TRIGGER AS $
BEGIN
INSERT INTO vendor_activity_log (
vendor_name,
operation,
data_accessed,
access_time,
source_ip
) VALUES (
NEW.vendor_name,
TG_OP,
TG_TABLE_NAME,
NOW(),
inet_client_addr()
);
-- Alert on suspicious activity
IF NEW.total_requests > NEW.rate_limit_per_hour THEN
INSERT INTO security_alerts (
alert_type,
vendor_name,
details
) VALUES (
'rate_limit_exceeded',
NEW.vendor_name,
jsonb_build_object(
'requests', NEW.total_requests,
'limit', NEW.rate_limit_per_hour
)
);
END IF;
RETURN NEW;
END;
$ LANGUAGE plpgsql;
Relevant Keywords
third party integration security weakness Symptoms: slow queries, data inconsistency, constraint violations Preventive: schema validation, constraint enforcement, proper typing Tech stack: PostgreSQL, MySQL, SQL Server, Oracle Industry: all industries, enterprise, SaaS