Generating and securing gift card codes
Asked Answered
J

4

10

I'm working for a company that is generating gift card codes which can be used to pay for goods on online stores.

I'm wondering what the most secure way of generating these gift card codes are. The length needs to be 16 characters (though that is negotiable) and can be alphanumeric (though numeric would be more customer friendly).

From what I can see, the most secure way to do this is generate a gift card code of a specific length with the following Java code:

static final String AB = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static SecureRandom rnd = new SecureRandom();

String randomString( int len ){
   StringBuilder sb = new StringBuilder( len );
   for( int i = 0; i < len; i++ ) 
      sb.append( AB.charAt( rnd.nextInt(AB.length()) ) );
   return sb.toString();
}

This is taken from the SO answer here. I removed the lowercase letters from the string to make it more user friendly. So this produces 36 ^ 16 combinations. Numeric alone would be 10 ^ 16 combinations. I believe numeric alone would be enough but it's often stressed that, given the increasing prevalence of gift card fraud, the string should be alphanumeric.

So that's question one: numeric or alphanumeric?

When users use the gift cards on an online store to pay for goods, a call is made to our API which returns the balance and currency for that gift card. Given that the gift card codes are entered on 3rd party servers, these gift cards are now available to people with access to those servers. This is obviously a problem in the case where there is still a balance left after a user has partially redeemed one.

One option would be to, when the call to our API is made (with the gift card code) to get the balance, we return and save on their store a random string which can only be used by the online store when they are billing us - we will match that with the gift card code on our system. The problem with that is presumably the gift card code the user enters on checkout gets logged somewhere in their logs, and is accessible to anyone with access to those logs.

Another option is that we refresh the gift card code after it is partially redeemed. So the user essentially gets issued with a new gift card code for the balance and the previous one is cancelled. This is probably the most secure, but not that user friendly.

So that's the second question: how do we secure gift card codes that are only partially redeemed and still have value left on them?

Jolenejolenta answered 3/4, 2018 at 10:2 Comment(11)
Regarding your partially redeemed keys : Why not deprecate the original key and create a new random key for the partially utilized gift card ? In effect - you recerate the gift card with a lower balance.Zak
Looks like the secure key generation issue is also nicely discussed here: #7112151Zak
@ErwinBolwidt I will place this one on security.stackexchange.com so. Yes it's a startup company with only 3 developers so we are free to decide this ourselvesJolenejolenta
@RannLifshitz yes one of the solutions i'm considering is to deprecate the original key and create a new one for the balance (outlined above in question)Jolenejolenta
@Jolenejolenta : I would definitly go with the deprecation and recreation of the key, based on balance change. This is a reduction of your problem - a key is associated with a balance, not a user (though you can use a DB to associate a user with multiple keys in order to record user actions). If usability is an issue - encapsulate the unique key - use a user token instead of the key. The token will be unique for the user (1-1 relationship), the encripted string will be unique for the balance, and the assocaition between the two will bedone on your backend.Zak
I hope I understand your concept correctly. But I wouldn't send informations such as giftcodes over third party servers. I would use a system like paypal or paysafecard. If a user want to pay something with a giftcard, he will be forwarded to your server to enter the code. You then notify the third party that the payment was successfully or not.Gilmer
@Gilmer Unfortunately that isn't an option. One option is encrypting the code on the client browser when they enter it on the online store, hitting our servers and returning some sort of key we store on the online store and associate with the payment..Jolenejolenta
@Jolenejolenta Do you have control over the online store? If not, this won't help much. The online store still has the code in plain text, not only the encrypted version. (Another problem) What if, the online store asks a user to pay 10$. Now the user enters a code with a value of 50$. The (evil) store could just send a request to your server to pay 50$ with the giftcode, that the user entered. You can't verify that the request contains only actions that the user accepted to do. Please ask this on security.stackexchange.com I'm very interested to see solutions to this question.Gilmer
@Gilmer we do to a certain extend, we have created a plugin which they install so we can control certain things. We only have 2 customers and haven't gone live yet, but have legal contracts in place. I have re-created this on Security Stack Exchange and already have an interesting answer security.stackexchange.com/questions/182840/…Jolenejolenta
@Gilmer this is a good point - after the user successfully makes an order, the online store makes a request to our servers with the redeem amount - this is done through our plugin which listens for successful orders. HmmmJolenejolenta
@Jolenejolenta : For the hash I would suggest using the user id + balance + date in order to ensure uniqueness.Zak
L
3

So the problem you are facing is an interesting problem. I read @Therac's solution to the problem and I would have to agree with him that you would end up creating a protocol similar to a crypto-currency. I also agree with all his cryptographic suggestions.

I will not repeat @Therac's solution, however, I will see if I can help by explaining some ideas from crypto-currencies. I will not go into much technical detail, but at a superficial level and you can judge for yourself if the idea holds merit for your use case.

So the data structure that most cryptocurrencies use is a Merkle hash tree. The idea is that they keep it as an append only log of transactions to verify previous transactions and that they are not being double spent.

So there are two types of transactions.

  1. CreateGiftCode
  2. SpendGiftCode

Create transactions are only valid if it is signed by your company. Thus you would store the amount you gave, the user's public address (Potentially his account number) and his gift card code.

The second kind of transaction would then be SpendGiftCode. This requires the person's gift card code and would require him to also sign the transaction to validate that the transaction is coming from him.

SpendGiftCode then consumes the gift card code completely (destroys the gift card code and stores that it's been used) and does one of two things:

  1. If the complete amount is spent, say a gift card of $50 is completely spent on a transaction, then a new giftcardcode is generated to the public address of the person he is paying to (which is the other parties account number).
  2. If the amount to be spent is less, say $10 out of $50, then two giftcardcodes are generated. One which is of $10 is sent to the other party and a new giftcardcode of the remaining amount is sent back to his account.

This would require account creation for your users and vendors but can help mitigate issues such as double spending and tracking. Since it is an append only log, you will be able to follow the trail of transactions every vendor and user makes. The merkle hash tree allows for optimizations in the log keeping.

Of course, there are several layers of technical difficulties I did not dive into and there are of course some plot holes in my explanation since I tried to provide a broad conceptual idea. Feel free to edit wherever you may see a mistake. Hope this was of some help,

Cheers!

Loam answered 7/4, 2018 at 6:32 Comment(2)
This is a nice approach you suggested here but it's most probably too far from the product's scopeMummy
@AndreiMarcut I agree. But since someone on another forum already gave a very good technical answer, I thought this answer might spark some ideas. For example, build the system on top of an Ethereum block chain and manage the computer nodes yourself (That too is probably far from the product's scope and would create a needless dependency). I do think that the idea of a 2 transaction type model is implementable though.Loam
H
1

Consider using OAuth 2.0 protocol with JWT. Authenticate user on shop side with OAuth and provide information about balance in the token. Don't provide any gift card codes or any crucial personal information. If you need to provide gift code (for customers investigations) generate any just to distinguish but don't use them for authentication. When a user makes transaction online shop need to authenticate them to receive the actual balance.

Hanako answered 10/4, 2018 at 6:15 Comment(0)
P
1

For the first question (numeric or alpha-numeric?) - If you focus on security (which I believe should be the case), then you want to increase the complexity of the coding alphabet so that a brute-force/guessing attacker would have to spend more effort to discover a valid gift code

By increasing the number of possibilities, the probability of guessing decreases (but it's still not bullet proof yet)

I recommend using alpha (case-sensitive) + numeric

In fact, you are more concerned here if the attacker can repeatedly attempt to guess your codes, rather than randomly guessing a code in a one-shot

To prevent that, you could leverage existing security mechanisms/concepts, such as:

(1) in the event of (your choice) 1-2-3-5 failed coupon calls, you could introduce an exponential retry delay for limiting the amount of consecutive attacks the same source could mount on your API

(2) set a captcha mechanism via http redirects, to prevent bots abusing after X (your choice) failed API hits

Also, since you are using random functions within a relatively low range of numbers, you'd want to double-check the uniqueness of the new generated code against the db before granting the code to the customer, to avoid an eventual collision which in turn could put your app in an unwanted state

For the second question, that sounds more like it's a business decision before choosing the security mechanisms. If you want to stay a coupon service you could fund the partner the entire amount and let him manage the balance, or you could preserve the remaining balance on the existing coupon in your own system, at the risk of the partner leaking your coupon code data which then would be a much bigger problem for you or you could as well issue another new coupon with the remaining balance (but then you have the challenge of associating it to an unknown user that could be easily distracted from this flow) otherwise you'd have to manage user financial accounts, which turns you into a different service..

Piccalilli answered 11/4, 2018 at 17:7 Comment(0)
J
1

I went through all the replies and came up with a solution, taking into account the suggestions:

  • We generate a link to send to the user. The key sent in the link is a random alphanumeric string and it is hashed (MD5 or something similar) so cannot be reveversed before being saved in the DB.

  • When user clicks on link, they are redirected to our landing page, we use the key to get the order, check the status of the order and whether there is credit on it, and if it's ok we generate a 16 character length alphanumeric code and send to the UI. The 16 character code is hashed (again MD5) and saved in our database. Each time a user clicks on the link, they see a new gift card code as it's generated on the fly each time.

  • In the contract with the online store, we specify they cannot log or save the gift card code anywhere (our 2 clients are large well known online retailers)

  • On our client's online store checkout page, to pay with our gift card code, the user provides the 16 character gift card code. It is sent to our servers and the balance and a random payment I.D. is returned to the online store. This payment I.D is saved on the online store as part of the order. On order complete, the online store sends an API request (with the payment I.D.) to our servers to redeem the amount from the gift card (this functionality has been built by us and is provided to the online store via a plugin they install).

  • communication between the online store and our API is authenticated using OAuth 2.0

  • If there is a balance left on the gift card, a new gift card code is generated (user is sent a new link to get their new gift card code for the balance)

  • When the online store is billing us, they provide us with a list of payment I.D.'s, which we then match to gift card codes in our backend (then matched to our issuer).

Protects:

  • The gift card code isn't sent to an email - just the link (in our backend we can do some checks - like see of the order has expired, has the credit already been used up, before displaying the gift card code)
  • Someone with access to the online store's DB will not see our gift card codes
  • Someone who has hacked our database cannot see gift card codes (as they are hashed), nor generate the link to see the gift card codes (as the key for the link is hashed)

Let me know if any comments.

Jolenejolenta answered 30/4, 2018 at 14:20 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.