SEC03-BP09 - Share resources securely with a third party
Implementation guidance
Sharing resources with third parties introduces additional security considerations beyond internal sharing. It’s essential to implement strong controls, monitoring, and governance to ensure that third-party access remains secure and compliant with your organization’s security policies.
Key steps for implementing this best practice:
-
Establish third-party access governance:
- Define policies for third-party access to your resources
- Implement approval processes for third-party access requests
- Document third-party relationships and access requirements
- Establish contractual security requirements for third parties
- Create procedures for onboarding and offboarding third parties
-
Implement secure access mechanisms:
- Use cross-account IAM roles with external IDs for third-party access
- Implement time-limited access with automatic expiration
- Use resource-based policies with specific conditions
- Apply network-level restrictions where possible
- Avoid sharing long-term credentials or access keys
-
Apply additional security controls:
- Implement multi-factor authentication requirements
- Use IP address restrictions for third-party access
- Apply time-based access controls
- Implement session monitoring and recording
- Use encryption for data shared with third parties
-
Monitor and audit third-party access:
- Track all third-party access activities
- Set up alerts for unusual access patterns
- Generate regular reports on third-party access
- Implement automated compliance checks
- Maintain detailed audit trails
-
Implement data protection measures:
- Classify data before sharing with third parties
- Apply appropriate encryption for shared data
- Implement data loss prevention (DLP) controls
- Use data masking or tokenization where appropriate
- Establish data retention and deletion policies
-
Regularly review and validate access:
- Conduct periodic reviews of third-party access
- Validate business justification for continued access
- Update access permissions based on changing requirements
- Remove access when no longer needed
- Test access revocation procedures
Implementation examples
Example 1: Cross-account role for third-party access with external ID
View code
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::THIRD-PARTY-ACCOUNT:root"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "UniqueExternalId-ThirdParty-2024"
},
"IpAddress": {
"aws:SourceIp": [
"203.0.113.0/24",
"198.51.100.0/24"
]
},
"DateGreaterThan": {
"aws:CurrentTime": "2024-01-01T00:00:00Z"
},
"DateLessThan": {
"aws:CurrentTime": "2024-06-30T23:59:59Z"
},
"Bool": {
"aws:MultiFactorAuthPresent": "true"
}
}
}
]
}Example 2: S3 bucket policy for secure third-party data sharing
View code
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ThirdPartyReadAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::THIRD-PARTY-ACCOUNT:role/DataProcessorRole"
},
"Action": [
"s3:GetObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::shared-data-bucket/third-party-data/*",
"arn:aws:s3:::shared-data-bucket"
],
"Condition": {
"StringEquals": {
"s3:x-amz-server-side-encryption": "aws:kms",
"s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012"
},
"Bool": {
"aws:SecureTransport": "true"
},
"IpAddress": {
"aws:SourceIp": "203.0.113.0/24"
},
"StringLike": {
"s3:x-amz-content-sha256": "*"
}
}
},
{
"Sid": "DenyUnencryptedObjectUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::shared-data-bucket/third-party-data/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
}
]
}Example 3: Lambda function for third-party access monitoring
View code
import json
import boto3
from datetime import datetime, timedelta
def lambda_handler(event, context):
"""
Monitor third-party access and generate security alerts
"""
# Initialize AWS clients
cloudtrail = boto3.client('cloudtrail')
sns = boto3.client('sns')
dynamodb = boto3.resource('dynamodb')
# Third-party account IDs to monitor
third_party_accounts = [
'THIRD-PARTY-ACCOUNT-1',
'THIRD-PARTY-ACCOUNT-2'
]
try:
# Look for third-party access events in the last hour
end_time = datetime.utcnow()
start_time = end_time - timedelta(hours=1)
response = cloudtrail.lookup_events(
StartTime=start_time,
EndTime=end_time
)
third_party_events = []
for event in response.get('Events', []):
event_detail = json.loads(event.get('CloudTrailEvent', '{}'))
# Check if this is third-party access
if is_third_party_access(event_detail, third_party_accounts):
third_party_events.append(event_detail)
# Analyze access patterns
analysis_result = analyze_third_party_access(third_party_events)
# Store access data for trend analysis
store_access_data(dynamodb, analysis_result)
# Send alerts if suspicious activity detected
if analysis_result.get('alerts'):
send_security_alerts(sns, analysis_result)
return {
'statusCode': 200,
'body': json.dumps({
'events_processed': len(third_party_events),
'alerts_generated': len(analysis_result.get('alerts', []))
})
}
except Exception as e:
print(f"Error monitoring third-party access: {str(e)}")
return {
'statusCode': 500,
'body': json.dumps(f'Error: {str(e)}')
}
def is_third_party_access(event_detail, third_party_accounts):
"""Check if the event represents third-party access"""
user_identity = event_detail.get('userIdentity', {})
account_id = user_identity.get('accountId')
return account_id in third_party_accounts
def analyze_third_party_access(events):
"""Analyze third-party access patterns for security issues"""
analysis = {
'total_events': len(events),
'unique_accounts': set(),
'unique_users': set(),
'alerts': []
}
for event in events:
user_identity = event.get('userIdentity', {})
source_ip = event.get('sourceIPAddress')
event_name = event.get('eventName')
analysis['unique_accounts'].add(user_identity.get('accountId'))
analysis['unique_users'].add(user_identity.get('arn'))
# Check for suspicious patterns
if is_suspicious_third_party_access(event):
analysis['alerts'].append({
'type': 'suspicious_access',
'event_name': event_name,
'source_ip': source_ip,
'user_arn': user_identity.get('arn'),
'timestamp': event.get('eventTime')
})
# Check for access outside allowed hours
if is_outside_allowed_hours(event):
analysis['alerts'].append({
'type': 'outside_hours_access',
'event_name': event_name,
'user_arn': user_identity.get('arn'),
'timestamp': event.get('eventTime')
})
analysis['unique_accounts'] = len(analysis['unique_accounts'])
analysis['unique_users'] = len(analysis['unique_users'])
return analysis
def is_suspicious_third_party_access(event):
"""Identify potentially suspicious third-party access"""
source_ip = event.get('sourceIPAddress')
event_name = event.get('eventName')
# Check for access from unexpected IP addresses
allowed_ips = ['203.0.113.0/24', '198.51.100.0/24'] # Define allowed IP ranges
if not any(ip_in_range(source_ip, allowed_ip) for allowed_ip in allowed_ips):
return True
# Check for high-risk actions
high_risk_actions = [
'DeleteBucket',
'PutBucketPolicy',
'CreateUser',
'AttachUserPolicy'
]
if event_name in high_risk_actions:
return True
return False
def is_outside_allowed_hours(event):
"""Check if access is outside allowed business hours"""
event_time = datetime.fromisoformat(event.get('eventTime').replace('Z', '+00:00'))
# Define allowed hours (9 AM to 6 PM UTC)
allowed_start = 9
allowed_end = 18
if event_time.hour < allowed_start or event_time.hour >= allowed_end:
return True
# Check if it's weekend
if event_time.weekday() >= 5: # Saturday = 5, Sunday = 6
return True
return False
def ip_in_range(ip, cidr):
"""Check if IP address is in CIDR range"""
import ipaddress
try:
return ipaddress.ip_address(ip) in ipaddress.ip_network(cidr)
except:
return False
def store_access_data(dynamodb, analysis):
"""Store access data for trend analysis"""
table = dynamodb.Table('ThirdPartyAccessLog')
table.put_item(
Item={
'timestamp': datetime.utcnow().isoformat(),
'total_events': analysis['total_events'],
'unique_accounts': analysis['unique_accounts'],
'unique_users': analysis['unique_users'],
'alert_count': len(analysis['alerts'])
}
)
def send_security_alerts(sns, analysis):
"""Send security alerts for suspicious third-party access"""
if not analysis['alerts']:
return
message = f"Third-party security alerts detected!\n\n"
message += f"Total events: {analysis['total_events']}\n"
message += f"Number of alerts: {len(analysis['alerts'])}\n\n"
for alert in analysis['alerts'][:5]: # Limit to first 5 alerts
message += f"Alert Type: {alert['type']}\n"
message += f"Event: {alert['event_name']}\n"
message += f"User: {alert.get('user_arn', 'Unknown')}\n"
message += f"Source IP: {alert.get('source_ip', 'Unknown')}\n"
message += f"Time: {alert['timestamp']}\n\n"
sns.publish(
TopicArn='arn:aws:sns:us-west-2:123456789012:ThirdPartySecurityAlerts',
Subject='Third-Party Access Security Alert',
Message=message
)Example 4: CloudFormation template for third-party access setup
View code
AWSTemplateFormatVersion: '2010-09-09'
Description: 'Third-party access setup with monitoring'
Parameters:
ThirdPartyAccountId:
Type: String
Description: AWS Account ID of the third party
ExternalId:
Type: String
Description: External ID for additional security
NoEcho: true
AllowedIPRange:
Type: String
Description: IP range allowed for third-party access
Default: '203.0.113.0/24'
Resources:
ThirdPartyAccessRole:
Type: AWS::IAM::Role
Properties:
RoleName: ThirdPartyAccessRole
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${ThirdPartyAccountId}:root'
Action: 'sts:AssumeRole'
Condition:
StringEquals:
'sts:ExternalId': !Ref ExternalId
IpAddress:
'aws:SourceIp': !Ref AllowedIPRange
Bool:
'aws:MultiFactorAuthPresent': 'true'
ManagedPolicyArns:
- 'arn:aws:iam::aws:policy/ReadOnlyAccess'
Tags:
- Key: Purpose
Value: ThirdPartyAccess
- Key: Environment
Value: Production
ThirdPartyAccessPolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: ThirdPartyAccessPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 's3:GetObject'
- 's3:ListBucket'
Resource:
- !Sub '${SharedDataBucket}'
- !Sub '${SharedDataBucket}/*'
Condition:
Bool:
'aws:SecureTransport': 'true'
Roles:
- !Ref ThirdPartyAccessRole
SharedDataBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'third-party-shared-data-${AWS::AccountId}'
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: aws:kms
KMSMasterKeyID: !Ref SharedDataKMSKey
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
NotificationConfiguration:
CloudWatchConfigurations:
- Event: 's3:ObjectCreated:*'
CloudWatchConfiguration:
LogGroupName: !Ref AccessLogGroup
SharedDataKMSKey:
Type: AWS::KMS::Key
Properties:
Description: 'KMS key for third-party shared data encryption'
KeyPolicy:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
AWS: !Sub 'arn:aws:iam::${AWS::AccountId}:root'
Action: 'kms:*'
Resource: '*'
- Effect: Allow
Principal:
AWS: !GetAtt ThirdPartyAccessRole.Arn
Action:
- 'kms:Decrypt'
- 'kms:GenerateDataKey'
Resource: '*'
AccessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: '/aws/third-party-access'
RetentionInDays: 90
Outputs:
ThirdPartyRoleArn:
Description: 'ARN of the third-party access role'
Value: !GetAtt ThirdPartyAccessRole.Arn
SharedBucketName:
Description: 'Name of the shared data bucket'
Value: !Ref SharedDataBucketAWS services to consider
AWS Identity and Access Management (IAM)
Enables you to manage access to AWS services and resources securely. Use IAM roles with external IDs for secure third-party access without sharing credentials.
AWS CloudTrail
Records API calls for your account and delivers log files to you. Essential for monitoring and auditing third-party access activities.
Amazon CloudWatch
Monitors your AWS resources and the applications you run on AWS in real time. Set up alerts and dashboards for third-party access monitoring.
AWS Key Management Service (KMS)
Makes it easy for you to create and manage cryptographic keys and control their use. Use KMS to encrypt data shared with third parties.
Amazon S3
Object storage service that offers industry-leading scalability, data availability, security, and performance. Use S3 bucket policies and encryption for secure data sharing with third parties.
AWS Config
Enables you to assess, audit, and evaluate the configurations of your AWS resources. Use Config rules to monitor compliance with third-party access policies.
Benefits of sharing resources securely with third parties
- Enhanced security: Maintains control over third-party access while enabling necessary collaboration
- Improved compliance: Supports regulatory requirements for third-party data sharing and access control
- Better risk management: Reduces risks associated with third-party access through proper controls
- Operational efficiency: Enables secure collaboration without compromising security posture
- Audit readiness: Provides comprehensive audit trails for third-party access activities
- Scalable governance: Establishes repeatable processes for managing third-party relationships
- Incident response: Enables quick identification and response to third-party security incidents