0
# Account Security Analysis
1
2
Advanced account protection features including related account group analysis, membership tracking, and search capabilities for detecting coordinated fraud attempts and abuse patterns. These capabilities help identify and mitigate sophisticated attacks that involve multiple related accounts.
3
4
## Capabilities
5
6
### List Related Account Groups
7
8
Retrieves related account groups that represent clusters of accounts with suspicious relationships, helping identify coordinated fraud attempts.
9
10
```python { .api }
11
def list_related_account_groups(
12
request: ListRelatedAccountGroupsRequest = None,
13
*,
14
parent: str = None,
15
retry: Union[retries.Retry, gapic_v1.method._MethodDefault] = _MethodDefault._DEFAULT_VALUE,
16
timeout: Union[float, object] = _MethodDefault._DEFAULT_VALUE,
17
metadata: Sequence[Tuple[str, str]] = ()
18
) -> ListRelatedAccountGroupsResponse:
19
"""
20
List groups of related accounts in a project.
21
22
Args:
23
request: The request object for listing related account groups
24
parent: Required. The name of the project in format 'projects/{project}'
25
retry: Retry configuration for the request
26
timeout: Timeout for the request in seconds
27
metadata: Additional metadata for the request
28
29
Returns:
30
ListRelatedAccountGroupsResponse: List of related account groups
31
32
Raises:
33
google.api_core.exceptions.PermissionDenied: If insufficient permissions
34
google.api_core.exceptions.InvalidArgument: If parent format is invalid
35
"""
36
```
37
38
#### Usage Example
39
40
```python
41
from google.cloud import recaptchaenterprise
42
43
client = recaptchaenterprise.RecaptchaEnterpriseServiceClient()
44
45
# List related account groups
46
request = recaptchaenterprise.ListRelatedAccountGroupsRequest(
47
parent="projects/your-project-id"
48
)
49
50
response = client.list_related_account_groups(request=request)
51
52
print("Related account groups:")
53
for group in response.related_account_groups:
54
print(f"Group: {group.name}")
55
print(f" Accounts in group: {len(group.memberships) if group.memberships else 'Unknown'}")
56
```
57
58
### List Related Account Group Memberships
59
60
Retrieves the memberships within a specific related account group, showing which accounts are connected.
61
62
```python { .api }
63
def list_related_account_group_memberships(
64
request: ListRelatedAccountGroupMembershipsRequest = None,
65
*,
66
parent: str = None,
67
retry: Union[retries.Retry, gapic_v1.method._MethodDefault] = _MethodDefault._DEFAULT_VALUE,
68
timeout: Union[float, object] = _MethodDefault._DEFAULT_VALUE,
69
metadata: Sequence[Tuple[str, str]] = ()
70
) -> ListRelatedAccountGroupMembershipsResponse:
71
"""
72
Get memberships in a related account group.
73
74
Args:
75
request: The request object for listing memberships
76
parent: Required. The group name in format 'projects/{project}/relatedaccountgroups/{group}'
77
retry: Retry configuration for the request
78
timeout: Timeout for the request in seconds
79
metadata: Additional metadata for the request
80
81
Returns:
82
ListRelatedAccountGroupMembershipsResponse: List of account memberships
83
84
Raises:
85
google.api_core.exceptions.NotFound: If the group doesn't exist
86
google.api_core.exceptions.PermissionDenied: If insufficient permissions
87
"""
88
```
89
90
### Search Related Account Group Memberships
91
92
Searches for account group memberships based on hashed account identifiers, enabling investigation of specific accounts.
93
94
```python { .api }
95
def search_related_account_group_memberships(
96
request: SearchRelatedAccountGroupMembershipsRequest = None,
97
*,
98
parent: str = None,
99
hashed_account_id: bytes = None,
100
retry: Union[retries.Retry, gapic_v1.method._MethodDefault] = _MethodDefault._DEFAULT_VALUE,
101
timeout: Union[float, object] = _MethodDefault._DEFAULT_VALUE,
102
metadata: Sequence[Tuple[str, str]] = ()
103
) -> SearchRelatedAccountGroupMembershipsResponse:
104
"""
105
Search for memberships in a related account group.
106
107
Args:
108
request: The request object for searching memberships
109
parent: Required. The project name in format 'projects/{project}'
110
hashed_account_id: Required. Hashed account identifier to search for
111
retry: Retry configuration for the request
112
timeout: Timeout for the request in seconds
113
metadata: Additional metadata for the request
114
115
Returns:
116
SearchRelatedAccountGroupMembershipsResponse: Search results
117
118
Raises:
119
google.api_core.exceptions.InvalidArgument: If search parameters are invalid
120
google.api_core.exceptions.PermissionDenied: If insufficient permissions
121
"""
122
```
123
124
#### Usage Example
125
126
```python
127
import hashlib
128
129
# Hash account identifier for search
130
account_id = "user123@example.com"
131
hashed_account_id = hashlib.sha256(account_id.encode()).digest()
132
133
# Search for related accounts
134
search_request = recaptchaenterprise.SearchRelatedAccountGroupMembershipsRequest(
135
parent="projects/your-project-id",
136
hashed_account_id=hashed_account_id
137
)
138
139
search_response = client.search_related_account_group_memberships(request=search_request)
140
141
print(f"Found {len(search_response.related_account_group_memberships)} related accounts")
142
for membership in search_response.related_account_group_memberships:
143
print(f"Group: {membership.name}")
144
print(f"Account: {membership.hashed_account_id.hex()}")
145
```
146
147
## Request and Response Types
148
149
### RelatedAccountGroup
150
151
```python { .api }
152
class RelatedAccountGroup:
153
"""Group of related user accounts."""
154
name: str # Output only. Resource name
155
memberships: List[RelatedAccountGroupMembership] # Account memberships in group
156
```
157
158
### RelatedAccountGroupMembership
159
160
```python { .api }
161
class RelatedAccountGroupMembership:
162
"""Membership in a related account group."""
163
name: str # Output only. Resource name
164
hashed_account_id: bytes # Hashed account identifier
165
account_id: str # Output only. Account identifier (if available)
166
```
167
168
### Request Types
169
170
```python { .api }
171
class ListRelatedAccountGroupsRequest:
172
"""Request message for listing related account groups."""
173
parent: str # Required. Project name in format 'projects/{project}'
174
page_size: int # Optional. Maximum results per page
175
page_token: str # Optional. Pagination token
176
177
class ListRelatedAccountGroupsResponse:
178
"""Response message for listing related account groups."""
179
related_account_groups: List[RelatedAccountGroup] # List of account groups
180
next_page_token: str # Token for next page of results
181
182
class ListRelatedAccountGroupMembershipsRequest:
183
"""Request message for listing account group memberships."""
184
parent: str # Required. Group name
185
page_size: int # Optional. Maximum results per page
186
page_token: str # Optional. Pagination token
187
188
class ListRelatedAccountGroupMembershipsResponse:
189
"""Response message for listing account group memberships."""
190
related_account_group_memberships: List[RelatedAccountGroupMembership] # Memberships
191
next_page_token: str # Token for next page
192
193
class SearchRelatedAccountGroupMembershipsRequest:
194
"""Request message for searching account group memberships."""
195
parent: str # Required. Project name
196
hashed_account_id: bytes # Required. Hashed account ID to search for
197
page_size: int # Optional. Maximum results per page
198
page_token: str # Optional. Pagination token
199
200
class SearchRelatedAccountGroupMembershipsResponse:
201
"""Response message for searching account group memberships."""
202
related_account_group_memberships: List[RelatedAccountGroupMembership] # Search results
203
next_page_token: str # Token for next page
204
```
205
206
## Usage Examples
207
208
### Account Investigation Workflow
209
210
```python
211
import hashlib
212
213
def investigate_account_relationships(client, account_identifier):
214
"""Investigate relationships for a specific account."""
215
216
# Hash the account identifier
217
hashed_id = hashlib.sha256(account_identifier.encode()).digest()
218
219
# Search for related account groups
220
search_request = recaptchaenterprise.SearchRelatedAccountGroupMembershipsRequest(
221
parent="projects/your-project-id",
222
hashed_account_id=hashed_id
223
)
224
225
search_response = client.search_related_account_group_memberships(request=search_request)
226
227
print(f"Investigating account: {account_identifier[:10]}...")
228
print(f"Found in {len(search_response.related_account_group_memberships)} related groups")
229
230
# Analyze each group the account belongs to
231
for membership in search_response.related_account_group_memberships:
232
group_name = membership.name.split('/')[3] # Extract group ID
233
print(f"\n--- Group {group_name} ---")
234
235
# Get all members of this group
236
list_request = recaptchaenterprise.ListRelatedAccountGroupMembershipsRequest(
237
parent=f"projects/your-project-id/relatedaccountgroups/{group_name}"
238
)
239
240
list_response = client.list_related_account_group_memberships(request=list_request)
241
242
print(f"Total members in group: {len(list_response.related_account_group_memberships)}")
243
244
# Show other members (first 5)
245
other_members = [m for m in list_response.related_account_group_memberships
246
if m.hashed_account_id != hashed_id]
247
248
print("Other members in group:")
249
for i, member in enumerate(other_members[:5]):
250
print(f" {i+1}. {member.hashed_account_id.hex()[:16]}...")
251
252
if len(other_members) > 5:
253
print(f" ... and {len(other_members) - 5} more")
254
255
# Investigate suspicious account
256
investigate_account_relationships(client, "suspicious.user@example.com")
257
```
258
259
### Fraud Detection Integration
260
261
```python
262
def check_account_risk_factors(client, assessment_response):
263
"""Check additional risk factors using related account analysis."""
264
265
# Extract account ID from assessment if available
266
if hasattr(assessment_response.event, 'hashed_account_id'):
267
hashed_account_id = assessment_response.event.hashed_account_id
268
269
# Search for related accounts
270
search_request = recaptchaenterprise.SearchRelatedAccountGroupMembershipsRequest(
271
parent="projects/your-project-id",
272
hashed_account_id=hashed_account_id
273
)
274
275
try:
276
search_response = client.search_related_account_group_memberships(request=search_request)
277
278
# Risk factors based on related accounts
279
group_count = len(search_response.related_account_group_memberships)
280
281
if group_count > 0:
282
print(f"Account belongs to {group_count} related account groups")
283
284
# Additional checks for high-risk patterns
285
for membership in search_response.related_account_group_memberships:
286
group_path = membership.name.rsplit('/', 1)[0] # Get group path
287
288
# Get group size
289
list_request = recaptchaenterprise.ListRelatedAccountGroupMembershipsRequest(
290
parent=group_path
291
)
292
293
list_response = client.list_related_account_group_memberships(request=list_request)
294
group_size = len(list_response.related_account_group_memberships)
295
296
if group_size > 10: # Large groups may indicate coordinated attacks
297
print(f"WARNING: Account in large group ({group_size} members)")
298
return "HIGH_RISK"
299
elif group_size > 5:
300
print(f"CAUTION: Account in medium group ({group_size} members)")
301
return "MEDIUM_RISK"
302
303
return "LOW_RISK"
304
else:
305
print("Account not found in any related groups")
306
return "UNKNOWN"
307
308
except Exception as e:
309
print(f"Error checking related accounts: {e}")
310
return "ERROR"
311
312
return "NO_ACCOUNT_ID"
313
314
# Use in assessment workflow
315
assessment = client.create_assessment(request=assessment_request)
316
account_risk = check_account_risk_factors(client, assessment)
317
318
print(f"Assessment score: {assessment.risk_analysis.score}")
319
print(f"Account risk level: {account_risk}")
320
```
321
322
### Bulk Account Analysis
323
324
```python
325
def analyze_account_groups(client, project_id):
326
"""Analyze all related account groups in a project."""
327
328
# Get all related account groups
329
list_request = recaptchaenterprise.ListRelatedAccountGroupsRequest(
330
parent=f"projects/{project_id}"
331
)
332
333
response = client.list_related_account_groups(request=list_request)
334
335
print(f"Found {len(response.related_account_groups)} related account groups")
336
337
group_stats = []
338
339
for group in response.related_account_groups:
340
# Get memberships for each group
341
memberships_request = recaptchaenterprise.ListRelatedAccountGroupMembershipsRequest(
342
parent=group.name
343
)
344
345
memberships_response = client.list_related_account_group_memberships(
346
request=memberships_request
347
)
348
349
group_size = len(memberships_response.related_account_group_memberships)
350
group_stats.append({
351
'name': group.name,
352
'size': group_size
353
})
354
355
# Sort by group size (largest first)
356
group_stats.sort(key=lambda x: x['size'], reverse=True)
357
358
print("\nLargest related account groups:")
359
for i, stats in enumerate(group_stats[:10]):
360
print(f"{i+1}. {stats['name']}: {stats['size']} members")
361
362
# Flag large groups for investigation
363
large_groups = [g for g in group_stats if g['size'] > 20]
364
if large_groups:
365
print(f"\nWARNING: {len(large_groups)} groups have >20 members - investigate for coordinated fraud")
366
367
return group_stats
368
369
# Analyze all groups
370
stats = analyze_account_groups(client, "your-project-id")
371
```
372
373
### Account Hashing Utilities
374
375
```python
376
import hashlib
377
import hmac
378
379
def hash_account_id(account_id, salt=None):
380
"""Hash account identifier for reCAPTCHA Enterprise."""
381
if salt:
382
# Use HMAC with salt for consistent hashing
383
return hmac.new(
384
salt.encode('utf-8'),
385
account_id.encode('utf-8'),
386
hashlib.sha256
387
).digest()
388
else:
389
# Simple SHA-256 hash
390
return hashlib.sha256(account_id.encode('utf-8')).digest()
391
392
def normalize_account_id(account_id):
393
"""Normalize account identifier before hashing."""
394
# Convert to lowercase and strip whitespace
395
normalized = account_id.lower().strip()
396
397
# Remove common variations (adjust for your use case)
398
# For email addresses
399
if '@' in normalized:
400
local, domain = normalized.split('@', 1)
401
# Remove dots from Gmail addresses
402
if domain in ['gmail.com', 'googlemail.com']:
403
local = local.replace('.', '')
404
normalized = f"{local}@{domain}"
405
406
return normalized
407
408
# Example usage
409
account_id = "User.Name@Gmail.com"
410
normalized_id = normalize_account_id(account_id) # "username@gmail.com"
411
hashed_id = hash_account_id(normalized_id)
412
413
print(f"Original: {account_id}")
414
print(f"Normalized: {normalized_id}")
415
print(f"Hashed: {hashed_id.hex()}")
416
```
417
418
### Integration with Assessment Creation
419
420
```python
421
def create_assessment_with_account_analysis(client, event_data, account_identifier=None):
422
"""Create assessment with related account analysis."""
423
424
# Hash account identifier if provided
425
hashed_account_id = None
426
if account_identifier:
427
hashed_account_id = hash_account_id(normalize_account_id(account_identifier))
428
429
# Create event with account information
430
event = recaptchaenterprise.Event(
431
token=event_data.get('token'),
432
site_key=event_data.get('site_key'),
433
user_agent=event_data.get('user_agent'),
434
user_ip_address=event_data.get('user_ip'),
435
expected_action=event_data.get('action'),
436
hashed_account_id=hashed_account_id
437
)
438
439
# Create assessment
440
assessment = recaptchaenterprise.Assessment(event=event)
441
assessment_request = recaptchaenterprise.CreateAssessmentRequest(
442
parent="projects/your-project-id",
443
assessment=assessment
444
)
445
446
assessment_response = client.create_assessment(request=assessment_request)
447
448
# Analyze related accounts if account ID was provided
449
account_analysis = None
450
if hashed_account_id:
451
account_analysis = check_account_risk_factors(client, assessment_response)
452
453
return {
454
'assessment': assessment_response,
455
'account_risk': account_analysis,
456
'risk_score': assessment_response.risk_analysis.score
457
}
458
459
# Example usage
460
event_data = {
461
'token': '03AIIukzh7Z...',
462
'site_key': '6LdGwQ0...',
463
'user_agent': 'Mozilla/5.0...',
464
'user_ip': '203.0.113.42',
465
'action': 'login'
466
}
467
468
result = create_assessment_with_account_analysis(
469
client,
470
event_data,
471
account_identifier="user@example.com"
472
)
473
474
print(f"Risk score: {result['risk_score']}")
475
print(f"Account risk: {result['account_risk']}")
476
```
477
478
## Error Handling
479
480
```python
481
from google.api_core import exceptions
482
483
try:
484
response = client.list_related_account_groups(request=request)
485
except exceptions.PermissionDenied as e:
486
print(f"Insufficient permissions for account analysis: {e}")
487
except exceptions.InvalidArgument as e:
488
print(f"Invalid request parameters: {e}")
489
490
try:
491
search_response = client.search_related_account_group_memberships(request=search_request)
492
except exceptions.InvalidArgument as e:
493
print(f"Invalid hashed account ID or search parameters: {e}")
494
except exceptions.NotFound as e:
495
print(f"No related account groups found: {e}")
496
```
497
498
## Best Practices
499
500
### Account Identifier Management
501
- Use consistent normalization before hashing account IDs
502
- Consider using HMAC with a secret salt for additional security
503
- Handle different account identifier formats (email, username, etc.)
504
- Document your hashing approach for consistency
505
506
### Privacy and Security
507
- Never store or log raw account identifiers
508
- Use hashed identifiers in all API calls
509
- Implement proper access controls for related account data
510
- Regular audit of who has access to account relationship data
511
512
### Analysis Workflows
513
- Integrate related account analysis with existing fraud detection
514
- Set appropriate thresholds for group size alerts
515
- Monitor trends in related account group formation
516
- Use related account data to enhance risk scoring models
517
518
### Performance Considerations
519
- Cache related account lookups for frequently analyzed accounts
520
- Implement pagination for large result sets
521
- Consider async processing for bulk account analysis
522
- Monitor API quotas and rate limits for account analysis operations