0033: Implement Secure Event Handler for Adjust Postbacks with DynamoDB Lookup
STATUS
[Accepted]
CONTEXT
The current Postback Processor system is vulnerable to URL manipulation fraud in our performance marketing platform. Fraudsters are exploiting the way event callbacks are configured in Adjust tracking links by manipulating the goal_id parameter in callback URLs to associate high-paying events with easier-to-complete events.
Currently, each event type has a separate callback URL with embedded goal IDs:
- event_callback_abc=ac_evcsws%26goal_id%3D123%26transaction_id%3D{transaction_id}
- event_callback_def=ac_evcsws%26goal_id%3D456%26transaction_id%3D{transaction_id}
Fraudsters modify these URLs to trigger high-value payouts for low-effort events:
- event_callback_abc=ac_evcsws%26goal_id%3D456%26transaction_id%3D{transaction_id}
This vulnerability undermines the integrity of our performance marketing system where players receive real money payments for legitimate app installs and actions.
The proposed solution implements a single callback endpoint at https://api.adaction.com/postbacks/adjust/consolidated_events that determines the correct tune_goal_id server-side using the immutable token_name provided by Adjust, eliminating the ability to manipulate goal associations through URL modification. This new endpoint will deprecate the existing https://api.adaction.com/postbacks/adjust/post_install_events endpoint.
Considered Options
Lookup Table Storage Options:
* Option 1: Store goal mappings in AWS DynamoDB with composite key <tune_offer_id>-<token_name>
* Option 2: Set up Aurora Serverless database instance to replicate Salesforce multireward billable data
* Option 3: Store lookup data as JSON file in S3
* Option 4: Create SQLite database file built into the Lambda image
* Option 5: Direct Redshift queries from Lambda (rejected due to performance concerns)
DECISION
We will implement a new secure event handler endpoint that:
- Accepts all event postbacks through a single callback URL instead of event-specific URLs
- Determines goal IDs server-side by looking up the
tune_goal_idusing the combination oftune_offer_idand Adjust'stoken_name - Uses DynamoDB for the lookup table with a composite key structure of
<tune_offer_id>-<token_name>
The DynamoDB option was selected because it provides: - Performance: Low-latency key-value lookups suitable for burst traffic (up to several thousand requests/minute) - Cost-effectiveness: Pay-per-request pricing model aligns with variable traffic patterns - Simplicity: Serverless nature requires no infrastructure management and integrates seamlessly with existing Lambda architecture - Scalability: Automatic scaling handles traffic bursts without configuration - Integration: Native CDK support for infrastructure-as-code deployment
The lookup data will be populated by extending the existing Airflow DAG that pulls Salesforce data, ensuring the DynamoDB table stays synchronized with the authoritative adaction_analysis.salesforce.multireward_billable_event table in Redshift.
Failure handling: - If goal_id lookup misses or DDB is unavailable handler will throw exception sending the message to the SQS dead letter queue for later playback.
CONSEQUENCES
Positive Outcomes: - Eliminates URL manipulation vulnerability by removing mutable goal IDs from callback URLs - Maintains existing architecture patterns (API Gateway → SQS → Lambda → Firehose → Redshift + TUNE forwarding) - Preserves performance with sub-millisecond DynamoDB lookups - Ensures data consistency through centralized goal ID determination - Reduces operational overhead with serverless storage solution
Negative Outcomes: - Introduces dependency on DynamoDB availability for postback processing - Adds complexity to the Airflow DAG for data synchronization - Creates potential single point of failure if DynamoDB lookup fails - Increases AWS service footprint and associated costs
Future Architecture Impact: - Establishes pattern for server-side business logic in Postback Processor - Creates reusable lookup table infrastructure for other anti-fraud measures - Enables more sophisticated event validation and processing capabilities
Risks
- Data synchronization lag: If Airflow DAG fails, DynamoDB may contain stale lookup data
- DynamoDB throttling: Unexpected traffic spikes could exceed provisioned capacity (mitigated by on-demand billing mode)
- We can consider a caching layer if DynamoDB throttling or cost become an issue.
- Lookup failures: Missing entries in DynamoDB would cause postback processing failures
- Increased latency: Additional DynamoDB lookup adds ~1-2ms to processing time
- Cost escalation: High traffic volumes could increase DynamoDB costs beyond projections
NOTES
References
- AWS DynamoDB Documentation
- Adjust Postback Documentation
- Existing Postback Processor CDK Infrastructure
- PR #79: docs: add ADR for secure event handler for Adjust postbacks
- PR #127: docs: backfill PR reference links for existing ADRs
Original Author
Ron White
Approval date
[To be filled]
Approved by
[To be filled]
Appendix
API Endpoints:
- New: https://api.adaction.com/postbacks/adjust/consolidated_events
- Deprecated: https://api.adaction.com/postbacks/adjust/post_install_events
Traffic Patterns: - Typical: 25 requests/minute - Burst: Several thousand requests/minute
DynamoDB Schema:
Primary Key: <tune_offer_id>-<token_name>
Attributes: tune_goal_id, last_updated
Cost Analysis (us-east-1):
- Monthly Operations: ~2.3M reads, ~3.6M writes (~5,000 records updated hourly)
-
On-Demand Pricing (recommended):
-
Read cost: ~$0.58/month
- Write cost: ~$4.50/month
-
Total: ~$5/month (~$6.50 during Christmas peak)
-
Storage: ~1MB (negligible cost)
- Rationale: On-demand billing optimal for bursty traffic patterns vs provisioned capacity
Integration Points: - Airflow DAG: Salesforce → Redshift → DynamoDB sync - Lambda: DynamoDB lookup → Firehose → TUNE forwarding