-2

Here is my request string that is currently being printed in the server log.

PaymentRequest{Id=123456, type=CREDIT_CARD, creditCardDetails=CreditCardDetails{type=VISA, name=Some Name, number=1234567890123456, expiry=0316, CCV=000}, directDebitDetails=null}

I want to create a mask for the number field in the above request so that when it is printed in the log it looks like 123456789012--HIDDEN-- (the last 4 digits are replaced with 'HIDDEN').

What java regex to create so that it robustly handles below situations?

  • If [field=value] i.e. number=1234567890123456 is missing in the String.
  • Credit card number could be of any length ranging from 0 to 16 e.g. number= OR number=0 or number=10 or number=1234567890123456.
  • If number is less than 4 than final output is number=--HIDDEN--
  • If number does not present i.e. number= than do nothing.
  • number=1234567890123456 could either be in the middle e.g. ..., name=Some Name, number=1234567890123456}, ... or at last e.g. ..., number=1234567890123456, name=Some Name}, ...
Kaizar Laxmidhar
  • 859
  • 1
  • 17
  • 38
  • @Kaizar : what is to be done when number is empty or contain less than 4 character ? – blackSmith Aug 13 '13 at 13:51
  • @blackSmith just do nothing, my purpose of considering that is that it should not throw exception if that is the case. – Kaizar Laxmidhar Aug 13 '13 at 13:54
  • You mean ranging from length 1 to 16 ? – Ibrahim Najjar Aug 13 '13 at 14:03
  • @Sniffer No, 0 to 16, I've just corrected. Thanks for asking clarification. – Kaizar Laxmidhar Aug 13 '13 at 14:07
  • So what if number is less than four digits, what to mask or hide then ? – Ibrahim Najjar Aug 13 '13 at 14:15
  • @Sniffer Thanks again! Added that too in the description. – Kaizar Laxmidhar Aug 13 '13 at 14:22
  • I would suggest you urgently look into PCI-DSS compliance, as your need to mask this data suggests you have the full card number available. You are also masking the data incorrectly (PCI allows you to mask all EXCEPT the last four digits for example). You should also not be persisting the CCV to disk. And you should be aware card numbers can be up to 19 digits in length. These are all serious breaches of payment card industry standards. – PaulG Aug 13 '13 at 16:44
  • @PaulG Tahnk you. Indeed I am going to mask CCV, exprity date and card type along with CC number. I only mentioned 'number' in the question to make the problem simpler. Once we get an idea how the number is masked it is just a matter of copying the solution for the rest of the fields. – Kaizar Laxmidhar Aug 13 '13 at 16:50

2 Answers2

2

OK you can do this using one line of code and it took me some time to figure out how but here is the result:

String input = "PaymentRequest{Id=123456, type=CREDIT_CARD, creditCardDetails=CreditCardDetails{type=VISA, name=Some Name, number=1234567890123456, expiry=0316, CCV=000}, directDebitDetails=null}"
String result = inputString.replaceAll("(?=number=\\d{1,16},)(number=\\d*?)\\d{1,4},", "$1--HIDDEN--,");
System.out.println(result);

Please let me know if there is any problems.

Ibrahim Najjar
  • 19,178
  • 4
  • 69
  • 95
  • @KaizarLaxmidhar I tested your input string and it working OK. The website you linked to is using Action Script 3 regular expression engine which may differ from the Java regular expression i am using, in other words it might work differently or may not support everything the java regex engine supports. – Ibrahim Najjar Aug 13 '13 at 19:56
  • @KaizarLaxmidhar Here is the Java source code file i used, [Download Here](http://www.4shared.com/file/IfmK9aLw/Prog.html). – Ibrahim Najjar Aug 13 '13 at 19:58
0

This is what you can do :

    String num="PaymentRequest{Id=123456, type=CREDIT_CARD, creditCardDetails=CreditCardDetails{type=VISA, name=Some Name, number=1234567890123456, expiry=0316, CCV=000}, directDebitDetails=null}";
    Pattern p = Pattern.compile("number=[0-9]*,");
    Matcher m = p.matcher(num); 
    m.find();
    System.out.println(num.replaceFirst(m.group(), m.group().replaceFirst("[0-9]{4},","****,")));

I wish I could come with a better solution.

blackSmith
  • 3,054
  • 1
  • 20
  • 37
  • I want to apply regex directly to the mentioned String without additional string manipulation. Hope I am not too ambitious. – Kaizar Laxmidhar Aug 13 '13 at 14:02
  • Yes a bit you are. For me its not possible in single regex. Updated the answer. – blackSmith Aug 13 '13 at 14:25
  • Though not in one shot as I wanted but your solution works. Thanks. Do you know how the pattern can be changed to handle the situation where number=1234567890123456 comes last in {} so it looks like {..., number=1234567890123456}, ... In summary the pattern which can handle either the number is between or at the last in the set. – Kaizar Laxmidhar Aug 13 '13 at 16:06
  • You have to remove the comma from the end of both the reg ex – blackSmith Aug 13 '13 at 19:25