feat(core): [Card Testing Guard] Implement Card Testing Guard #7108
feat(core): [Card Testing Guard] Implement Card Testing Guard #7108Gnanasundari24 merged 42 commits intomainfrom
Conversation
3f5df26 to
6533e83
Compare
crates/common_utils/src/consts.rs
Outdated
There was a problem hiding this comment.
| pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY: i32 = 3600; | |
| pub const DEFAULT_CARD_TESTING_GUARD_EXPIRY_IN_SECS: i32 = 3600; |
There was a problem hiding this comment.
I have resolved it, thanks
4ec51bc to
17776e5
Compare
1ff0d20 to
b871ec9
Compare
crates/api_models/src/admin.rs
Outdated
There was a problem hiding this comment.
| pub struct CardTestingGuardConfig { | |
| /// Determines if Card IP Blocking is enabled for profile | |
| pub is_card_ip_blocking_enabled: bool, | |
| /// Determines the unsuccessful payment threshold for Card IP Blocking for profile | |
| pub card_ip_blocking_threshold: i32, | |
| /// Determines if Guest User Card Blocking is enabled for profile | |
| pub is_guest_user_card_blocking_enabled: bool, | |
| /// Determines the unsuccessful payment threshold for Guest User Card Blocking for profile | |
| pub guest_user_card_blocking_threshold: i32, | |
| /// Determines if Customer Id Blocking is enabled for profile | |
| pub is_customer_id_blocking_enabled: bool, | |
| /// Determines the unsuccessful payment threshold for Customer Id Blocking for profile | |
| pub customer_id_blocking_threshold: i32, | |
| /// Determines Redis Expiry for Card Testing Guard for profile | |
| pub card_testing_guard_expiry: i32, | |
| } | |
| pub struct CardTestingGuardConfig { | |
| /// Determines if Card IP Blocking is enabled for profile | |
| pub card_ip_blocking: Enabled/Disabled, | |
| /// Determines the unsuccessful payment threshold for Card IP Blocking for profile | |
| pub card_ip_blocking_threshold: i32, | |
| /// Determines if Guest User Card Blocking is enabled for profile | |
| pub guest_user_card_blocking: Enabled/Disable, | |
| /// Determines the unsuccessful payment threshold for Guest User Card Blocking for profile | |
| pub guest_user_card_blocking_threshold: i32, | |
| /// Determines if Customer Id Blocking is enabled for profile | |
| pub customer_id_blocking: Enabled/Disabled, | |
| /// Determines the unsuccessful payment threshold for Customer Id Blocking for profile | |
| pub customer_id_blocking_threshold: i32, | |
| /// Determines Redis Expiry for Card Testing Guard for profile | |
| pub card_testing_guard_expiry_in_secs: i32, | |
| } | |
| Avoid using boolean instead use enum, and add unit when it comes to SI units. |
There was a problem hiding this comment.
I have resolved it, thanks
| /// * The Fingerprint encryption operation fails | ||
| pub async fn get_merchant_profile_fingerprint_secret( | ||
| state: &SessionState, | ||
| merchant_id: &common_utils::id_type::MerchantId, |
There was a problem hiding this comment.
Pass merchant_key_store as a argument to this function, utils function shouldn't read data from database and already merchant key store is populated in the handler after authentication
There was a problem hiding this comment.
Since we are no longer generating cardtesting_secret_key in oltp flow, this function has been removed.
|
|
||
| match merchant_card_testing_secret_key { | ||
| Some(card_testing_secret_key) => Ok(card_testing_secret_key.get_inner().clone().expose()), | ||
| None => { |
There was a problem hiding this comment.
If there secret is not there we should throw error, we can't randomly create a key and insert that to profiles in OLTP flow. Profiles will get updated only in admin flows.
There was a problem hiding this comment.
I have resolved it, thanks. The card_testing_secret_key will now only be updated in profile create and update flows.
crates/router/src/core/payments.rs
Outdated
| pub card_testing_guard_data: Option<CardTestingGuardData>, | ||
| } | ||
|
|
||
| #[derive(Clone, serde::Serialize, Debug)] |
There was a problem hiding this comment.
Move this to domain_models, avoid writing types in payments.rs file.
There was a problem hiding this comment.
I have resolved it, thanks
| } | ||
| } | ||
| Ok(None) => Ok(cache_key), | ||
| Err(_) => Err(errors::ApiErrorResponse::InternalServerError)?, |
There was a problem hiding this comment.
Avoid discarding the error, the error should be lifted into internalservererror.
There was a problem hiding this comment.
I have resolved it, thanks
| } | ||
| } | ||
| Ok(None) => Ok(cache_key), | ||
| Err(_) => Err(errors::ApiErrorResponse::InternalServerError)?, |
There was a problem hiding this comment.
Avoid discarding the error, the error should be lifted into internalservererror.
There was a problem hiding this comment.
I have resolved it, thanks.
| } | ||
| } | ||
| Ok(None) => Ok(cache_key), | ||
| Err(_) => Err(errors::ApiErrorResponse::InternalServerError)?, |
There was a problem hiding this comment.
Avoid discarding the error, the error should be lifted into internalservererror.
There was a problem hiding this comment.
I have resolved it, thanks
| let unsuccessful_payment_threshold = card_testing_guard_config.card_ip_blocking_threshold; | ||
|
|
||
| match services::card_testing_guard::get_blocked_count_from_cache(state, &cache_key).await { | ||
| Ok(Some(unsuccessful_payment_count)) => { |
There was a problem hiding this comment.
Handling response is same for all validate functions means, refactor the code and avoid repetition.
There was a problem hiding this comment.
I have resolved it, thanks
There was a problem hiding this comment.
Don't mutate paymentData in this function, instead pass the output, in the caller update PaymentData
There was a problem hiding this comment.
I have resolved it, thanks.
554629d
…tion-fork * 'main' of github.com:juspay/hyperswitch: feat(core): [Card Testing Guard] Implement Card Testing Guard (juspay#7108) chore(version): 2025.02.25.0 ci: minor refactors and improvements (juspay#7308) fix(connector): [NMI] Added enum for Void Reason (juspay#7221) ci(CI-pr): disable `cargo-hack` step for hotfix PRs (juspay#7334)
Type of Change
Description
Problem Statement:
Card testing attacks involve fraudsters using stolen credit card details to make small transactions on e-commerce sites using different combinations of CVC to verify if the CVC is valid. Successful transactions indicate the card is active and the CVC is valid, while failed ones are discarded. This is done to avoid detection before committing larger fraudulent purchases.
Solution:
We have implemented three rules for detecting and preventing this card testing attacks:
I) Card <> IP Blocking for Merchant : If there are X number of unsuccessful payment attempts from a single IP Address for a specific merchant, then that combination of Card <> IP will be blocked for that merchant for a particular duration.
II) Card Blocking for Guest Users for Merchant: If there are X number of unsuccessful payment attempts for a single card, then guest user payments will be blocked for that card and that merchant for a particular duration. Logged in customers will still be able to use that card for payment.
III) Customer ID Blocking for Merchant: If there are X number of unsuccessful payment attempts from a single Customer ID, then that customer ID will be blocked from making any payments for a particular duration.
These unsuccessful payment thresholds and duration for blocking is configurable and stored in database.
Whether the merchants want these rules to be enabled/disabled, that is also configurable.
Implementation:
The attacker gets the
client_secretfrom the payment intent create call which happens through our SDK, and uses theclient_secretto hit the /confirm API repeatedly with different sets of card numbers and CVC.A method
validate_request_with_statehas been created in theGetTrackertrait which enforces the three above mentioned rules depending on the business_profile config. Thevalidate_request_with_state methodinternally callsvalidate_card_ip_blocking_for_business_profilemethod,validate_guest_user_card_blocking_for_business_profilemethod andvalidate_customer_id_blocking_for_business_profilemethod for performing the below validations:I) Card<>IP Blocking: A fingerprint is generated for the card which is unique to the
business_profile(by using a hash key which is unique to the profile and stored ascard_testing_secret_keyinbusiness_profile). This is done so that the cards are blocked at a profile level. The IP Address is fetched from the payment intent create request, and then for each unsuccessful payment attempt, the value of the redis key CardFingerprint<>IP is increased by 1, and after it reaches a certain threshold, it is blocked.II) Guest User Card Blocking: A fingerprint is generated for the card which is unique to the
business_profile(by using a hash key which is unique to the profile and stored ascard_testing_secret_keyinbusiness_profile). This is done so that the cards are not blocked at a profile level. For each unsuccessful payment attempt, the value of the redis key CardFingerprint is increased by 1, and after it reaches a certain threshold, it is blocked, but only for guest users. Logged in customers can still make payments with that card.III) Customer ID Blocking: The
customer_idis fetched from the payment intent create call request, and then for each unsuccessful payment attempt, the value of redis key CardFingerprint<>Profile is increased by 1, and after it reaches a certain threshold, it is blocked.Additional Changes
Motivation and Context
How did you test it?
Postman Tests
Step By Step Guide to Test:
card_ip_blocking_statusasenabled,guest_user_card_blocking_statusasdisabledandcustomer_id_blocking_statusasdisabledcard_ip_blocking_threshold), the payment attempt will be blocked.card_ip_blocking_statusasdisabled,guest_user_card_blocking_statusasenabledandcustomer_id_blocking_statusasdisabledguest_user_card_blocking_threshold), the payment attempt will be blocked.card_ip_blocking_statusasdisabled,guest_user_card_blocking_statusasdisabledandcustomer_id_blocking_statusasenabledcustomer_id_blocking_threshold), the payment attempt will be blocked.Update Business Profile (For enabling card_ip_blocking)
-Request
-Response
1. Card <> IP Blocking (Below Threshold)
-Request
-Response
2. Card <> IP Blocking (After threshold)
-Request
-Response
3. Guest User Card Blocking (Below Threshold)
-Request
-Response
4. Guest User Card Blocking (After Threshold & Guest User)
-Request
-Response
5. Customer ID Blocking (Below Threshold)
-Request
-Response
6. Customer ID Blocking (After Threshold)
-Request
-Response
Cypress Tests
Checklist
cargo +nightly fmt --allcargo clippy