logo

Not Storing Credit Card Numbers In Domino

For the past few weeks I've been adding a shopping cart to an existing Domino application. The existing system stored credit card numbers in plain text, which - as I'm sure you know - is a massive huge great no-no when it comes to being PCI DSS compliant.

I'll talk more about how I implemented a gateway part of the cart another day. For now I want to discuss how I avoided, at all costs, storing the credit card number entered by the user.

Normally, to stop a field being saved on the backend document you'd just make it a Computed For Display field, right? Like this:

image

The field itself is hidden, but we display an input field of the same name to the user using Passthru HTML. This way they can enter a value, we can reference it in our WebQuerySave code and it never gets stored.

Normally it would be as simple as that, but in my case things were complicated. The developer of the existing system had turned on the "Generate HTML For All Fields" Form option. I had no idea why and didn't dare turn it off for fear of what affect this might have on what was already a monstrously-complicated system.

The trouble with the "generate all fields" Form option is that as well as our own PassThru field Domino generates another (type="hidden") one towards the bottom of the HTML form with the same name. It does this because our CFD field is hidden.

When the Form is submitted to the web server the server only accepts the value from the bottom-most field (always ""), so the value entered by the user is never available to us.

To get round this I could use JavaScript to see if there are two fields called CreditCardNumber and, if so, set the value of the second to the value of the first before submitting the form.

var f = document.forms[0];
if (f.CreditCardNumber && f.CreditCardNumber.length>0){
 f.CreditCardNumber[1].value = f.CreditCardNumber[0].value;
}

BUT. Could I live with myself if I was responsible for an e-commerce site that relied on JavaScript to work? Those who know me well enough will know the answer is a resounding NO!!!

The Only Other Solution

My only option was to un-hide the CreditCardNumber field and make it editable. The issue then became how to make sure it never ever ever got stored on the document. To do this I removed it at the very end of the Java WQS agent. Here's how:

public class JavaAgent extends AgentBase {
 private Document document;

 public void NotesMain() {
  try {
   Session session = getSession();
   AgentContext agentContext = session.getAgentContext();
   Database database = session.getCurrentDatabase();
   document = agentContext.getDocumentContext();

   //Do the credit card processing part here.

  } catch(Exception e) {
   e.printStackTrace();
  } finally {
   try{
    document.removeItem("CreditCardNumber");
   }catch(Exception ex) {
    ex.printStackTrace();
   }
  }
 }
}

Notice how we defined the contextual Document outside the NotesMain function? This is so that we can refer to it in the "finally" section. Code inside the "finally" clause will run even if the main code section results in an error for any reason. So, it almost guarantees the field will get removed during saving the document.

Or will it. What if!! What if the Agent fails to run for some reason other than an error in the code? Say the Agent times-out or the server is under heavy traffic and it fails to run completely. What happens then!?

To get round the idea that the Agent might fail to run I added an extra line of @Formula after the call to run the Agent in the Form's WebQuerySave event, like so:

image

As far as I can tell this is almost fool-proof. What say you? Am I missing something here or can I sleep soundly at night, safe in the knowledge this will always remove the field?

Comments

  1. If the "(WebProcessPayment)" agent fails in someway that you don't catch the @SetField won't get run.

    Having been down this road over the past 13 years with my eCommerce site, can I just state one thing which you really should make clear for the best security. The Credit Card info (and personal info) should NEVER be stored in the database which is used to run the website.

    That never as in don't save it, don't view it, don't edit it. Having talked at UKLUG on the subject of security of Domino sites, having a job in security, having had over £8.2 millon transactions through my sites and having hacked more than a few websites (in the white hat sense) you should never store the info even for an instant.

    Customer info is sacrosanct. It is the make or break of any business. Once it gets out you face more trouble than you know. Don't risk it.

    The trick here is to use you website as a pass through to a 2nd database where all the info is kept under lock and key and is encrypted to the hilt. Submit to an agent and let that handle the necessary workings. Never save to a document under any circumstances.

      • avatar
      • Jake Howlett
      • Wed 14 Sep 2011 07:06 AM

      Are you saying that if the Agent called from line 1 of the @Formula in the Form's WebQuerySave Event fails for any reason then the second line of the formula (the @SetField) won't be called?

      In this scenario does the document itself get committed to the database at all?

      Another idea I had was to set SaveOptions field to "0" on the form submitted and only change it to "1" at the end of the Java agent (after the ccnumber field was removed).

      I concur entirely with your approach to not storing CC data. But I'm between a rock and a hard place. You wouldn't believe what a beast of a system the previous developer had come up with. Turning off the "generate all field" option is what I want to do. I just daren't do it.

      Show the rest of this thread

      • avatar
      • Jake Howlett
      • Wed 14 Sep 2011 07:16 AM

      The other option is I go back to the hidden CFD field and use JavaScript to switch values between the fields at the point the form's submitted. I'd rather claw out my own eyeballs though.

      Another option is a scheduled agent to look for documents that do have the field on them and let it regularly purge the fields from them.

  2. Hear hear. When implementing a shopping cart at a privious engaement I opted for using an external party (Ogone) to handle the creditcard stuff.

    1. Mark Barton and I integrated successfully with PayPal for the same purpose. The trick was converting their php handshake file to a java agent to work properly - but that was 5 years ago now. :-(

  3. Hi

    Try this:

    In "Input Translation" for the field just put what ever you want saved in the field instead of what gets posted from the browser.

    /brgds Jesper Kiaer

    http://nevermind.dk

      • avatar
      • Jake Howlett
      • Wed 14 Sep 2011 07:10 AM

      But then the Java agent in the WQS can't read the cc number.

  4. To extend on Dragon's comments, the personal information should not even be stored on any server that is accessible from the web.

    I'm working on a credit card processing site for work. We added in TWO new domino servers, each in their own Domino domains and only one of them accessible via http in our dmz, the second is on internal network only, has no http etc.

    The two servers are cross certified and when the user enters in credit card and personal data the data is captured via agent and moved to the storage database on the other server.

      • avatar
      • Jake Howlett
      • Wed 14 Sep 2011 07:10 AM

      Why store the data at all though? On the system I'm working on the Java agent communicates over a URLConnection with a 3rd party gateway which returns a success/failure message and all happens over an SSL connection. No need to store the cc number at all. Passes the responsibility for PCI compliance to somebody else.

    • avatar
    • Partha
    • Wed 14 Sep 2011 08:40 AM

    How about this?

    Set item = doc.GetFirstItem("CCNumber")

    item.SaveToDisk = False

      • avatar
      • Jake Howlett
      • Wed 14 Sep 2011 08:41 AM

      But what if the agent that that code is in fails to run?

      Show the rest of this thread

  5. Have you tried putting /form (close form tag) at the bottom of the notes form. If I remember correctly, the hidden fields are generated after your markup. Then the auto-generated field should be ignored in the submit.

      • avatar
      • Jake Howlett
      • Wed 14 Sep 2011 02:32 PM

      Hey Tommy! Long time!!

      Closing the form tag like would, in effect, be just the same as turning off the "generate all fields" property on the form. I tried this and all hell broke loose and it just stopped working all together. The old developer used extensive JavaScript that refers to (and sets) the values in these fields. Now, I could re-write all his code but you would NOT believe what a mess it is and I just don't have the scope to mess about with it.

      Hide the rest of this thread

      1. I was afraid of that. The only other way that I can think of (that works without JavaScript) is to trick the form into thinking that the field is a checkbox (using a "fake" field similar to one of those voodoo fields that Domino generates), so that it accepts multiple values. I haven't tried anything like that myself, so I don't know if it's even possible.

        I still follow you blog, but it's been a while since I've had

        anything to contribute due to non-domino content.. :)

          • avatar
          • Jake Howlett
          • Wed 14 Sep 2011 02:48 PM

          I tried making the hidden CFD field multi-value. Normally when the browser submits two key-value pairs with the same key name the Domino web server stores both items in the same field as a text list. However, this only works if the two key-value pairs are submitted one after the other. If other key-value pairs are submitted between them then Domino stores only the last pair it receives.

            • avatar
            • Mark Vincenzes
            • Mon 19 Sep 2011 12:04 PM

            For 8.5.1 there is an INI that modifies the POST behavior for non consecutive duplicate name INPUT tags.

            See http://www-10.lotus.com/ldd/r5fixlist.nsf/Public/0988479A84A5C6CF85257632004EB18D?OpenDocument

            Unfortunately it affects the entire server, not just one application.

    • avatar
    • Jan Sandholm
    • Wed 14 Sep 2011 12:11 PM

    Whatever the initial in-process solution is, you might also want to add another mechanism to make sure possible errors in the initial processing won't become catastrophic: add a scheduled agent (5 min) to process all new or modified documents and clear the credit card field in case it's not already cleared (as it should be). Make the agent send you an email as well to alarm you of potential trouble in the initial field handling.

  6. I am always very wary of scheduled agents doing anything vital. All sorts of things can stop them from running.

    • avatar
    • Patrick Niland
    • Thu 15 Sep 2011 05:09 PM

    Hey Jake,

    Another way to do this is to create the form and fields through HTML and post this HTML form to an agent. This way you are not actually saving a document on the submit, you are just posting the data to the agent. It is then up to your agent, as to how you process the data. This way you can 100% guarantee that the record will only be processed, once the agent runs successfully.

    From your code, you appear to be running Java for your agent. With the above solution, you could implement a Java Servlet to handle all the data.

    Later

    Patrick

Your Comments

Name:
E-mail:
(optional)
Website:
(optional)
Comment:


About This Page

Written by Jake Howlett on Wed 14 Sep 2011

Share This Page

# ( ) '

Comments

The most recent comments added:

Skip to the comments or add your own.

You can subscribe to an individual RSS feed of comments on this entry.

Let's Get Social


About This Website

CodeStore is all about web development. Concentrating on Lotus Domino, ASP.NET, Flex, SharePoint and all things internet.

Your host is Jake Howlett who runs his own web development company called Rockall Design and is always on the lookout for new and interesting work to do.

You can find me on Twitter and on Linked In.

Read more about this site »

More Content