Forum Discussion

BlurredVision_1's avatar
BlurredVision_1
Icon for Nimbostratus rankNimbostratus
Jan 30, 2008

CCN Scrubber not matching card numbers...

All,

 

I am working on an iRule demo to show people how darn powerful these things are. The CCN Scrubber is an ideal candidate. Only thing is I can't get it working.

 

 

As I am putting together a demo that others will be able to also run, I am using an online page with sample test card number that are valid (as far as the LUHN check is concerned...) located here:

 

 

only issue is that the card_indices value is being returned as empty:

 

 

Find ALL the possible credit card numbers in one pass

 

set card_indices [regexp -all -inline -indices {(?:3[4-7]\d{13})|(?:4\d{15})|(?:5[1-5]\d{14})|(?:6011\d{12})} [HTTP::payload]]

 

log local0. "card indices are \"$card_indices\""

 

 

returns the following in the logs:

 

 

Rule CC_scrub : card indices are ""

 

 

Any ideas why the regex is not matching a thing?

 

 

Cheers.

 

 

 

Blurred.

7 Replies

  • Hi,

    The regex you have listed matches the sample cards on the page. Are the server responses compressed? Can you log any portion of the response using log commands?

    Else, another option...

    The CC scrubber rule collects the response data in order to apply the regex and remove the unwanted strings. I think you'd see significant performance improvement using an iRule and a stream profile instead. The stream profile provides search/replace functionality as the HTTP data is passed through. The search parameter in a stream expression can be a regex.

    If you're on a version lower than 9.4.0, you need to set response chunking to rechunk on the HTTP profile as described in SOL6422 / CR53771. Also, due to a bug described in SOL6741 / CR70146 (fixed in 9.4.0), TMM restarts if the response is over 4Mb.

    
    when RULE_INIT {
        This regex defines what strings are considired a credit card.  Wrap the regex in curly braces.
       set ::cc_regex {(?:3[4-7]\d{1,3})|(?:4\d{1,5})|(?:5[1-5]\d{1,4})|(?:6011\d{1,2})}
        Replace the matched strings with this string.  It can be blank to remove the string altogether.
       set ::replacement_text "xxxxxxxxxxxxxxxx"
        As an example, this is a way to limit which requests to check the responses from.  
       set ::uris_to_check_response [list \
          .aspx \
          .asp \
          .html \  
       ]
        Log debug to /var/log/ltm?  1=yes, 0=no.
       set ::cc_replace_debug 1
    }
    when HTTP_REQUEST {
        Don't check responses by default
       set check_response 0
        Check if response 
       if {[matchclass [string tolower [HTTP::path]] ends_with $::uris_to_check_response]}{
          set check_response 1
       }
    }
    when HTTP_RESPONSE {
        Disable the stream filter by default
       STREAM::disable
        Check the response if the response we want to check.  You can check all text responses, and/or based on the request type
       if {[HTTP::header value Content-Type] contains "text" and $check_response}{
           Don't apply the stream profile against 4+Mb response sizes or TMM will restart (reference: SOL6741 / CR70146)
           You can remove this check if your version has a fix for this issue.
          if {[HTTP::header exists Content-Length] and [HTTP::header value Content-Length] < 4194304}{
              Wildcard match
             set stream_expression {@$::cc_regex@$::replacement_text@}
              Set the find/replace strings
             STREAM::expression $stream_expression
             if {$::debug}{  
                log local0. "Current stream expression: $stream_expression"
             }  
              Enable the stream filter for this response
             STREAM::enable
          }
       }   
    }
     STREAM_MATCHED is triggered when the stream filter's find string is found
    when STREAM_MATCHED {
        Log the string which matched the stream profile
       if {$::debug}{   
          log local0. "Matched: [STREAM::match]"
       }
    }

    Note, I haven't tested this version of a rule. It's adapted from a related one I've used in the past. If you try this and run into any issues, please let me know.

    Thanks,

    Aaron
  • The rule is still not working for me, but I found the first culprit: compression

     

     

    to turn it off, first create an http profile for your VS that sets compression to "Selective", then, turn off compression in your HTTP_REQUEST:

     

     

    when HTTP_REQUEST {

     

    Don't allow data to be chunked

     

    if { [HTTP::version] eq "1.1" } {

     

    if { [HTTP::header is_keepalive] } {

     

    HTTP::header replace "Connection" "Keep-Alive"

     

    }

     

    HTTP::version "1.0"

     

    }

     

    COMPRESS::disable <-- This is the line you add..

     

    }

     

     

    Now I am getting payload that is uncompressed. Only issue is, I am getting payload I don't care about (ie: gif, jpg etc..)

     

     

  • You can apply the response logic if the Content-Type header contains the string text:

     

     

    ...

     

    if {[HTTP::header value Content-Type] contains "text"}

     

    ...

     

     

    Aaron
  • I am running v9.4.2. I think the issue to get this working (optimisations aside for the moment, this is just to show it working as a Proof of Concept) is going to be Content-Type evaluation so we are only validating text content...

    Joe mentioned in his 2006 post :

    Good point. I've updated the wiki sample with a check to only test for text based responses (ie. Content-Type starts_with "text/"). If you need to be more specific, you could easily create a data group containing the content types you want to check for and replace my conditional with a matchclass command comparing the content-type to a member of the data group.

    but there does not seem to be a Content-Type check in the wiki listed iRule any more.

    Ill try this and see how I go:

    when HTTP_RESPONSE {
    if { [HTTP::header "Content-Type"] starts_with "text/" } {
  • Sorted: the Content-Type check was what it needed.

    I inadvertently grabbed the iRule I was playing with from Joe's original blog post, and not from the wiki. Writing iRules for 16 hours will do that to you..

    Looks like the wiki listed iRule could do with some credit card number logic touch ups to cover more than VISA/MASTERCARD/AMEX/DISCOVER. It still misses Diners Club Cards:

    IssuerIdentifierCard Number Length

    Diner's Club/Carte Blanche 300xxx-305xxx14

    36xxxx 14

    38xxxx14

    American Express 34xxxx15

    37xxxx 15

    VISA 4xxxxx 13, 16

    MasterCard 51xxxx-55xxxx 16

    Discover 6011xx16

    I have modified the regex to match 13 digit Visa cards (which apparently exist?!?!) but I can't get it to match Diners cards (the 300-305 and 36,38 prefixes)

    set card_indices [regexp -all -inline -indices {(?:3[00-05]\d{11})|(?:3[6|8]\d{11})|(?:3[4|7]\d{13})|(?:4\d{12})|(?:4\d{15})|(?:5[1-5]\d{14})|(?:6011\d{12})} [HTTP::payload]]  
  • lets try that again. This works now:

      set card_indices [regexp -all -inline -indices {(?:30[0-5]\d{11})|(?:3[6|8]\d{12})|(?:3[4|7]\d{13})|(?:4\d{12})|(?:4\d{15})|(?:5[1-5]\d{14})|(?:6011\d{12})} [HTTP::payload]] 

    Ill update the wiki sample with the bigger regex..

    thanks for the help hoolio!!
  • No problem. At some point I'll try testing the stream version and post a working version as an alternative.

     

     

    Thanks for updating the wiki with what you found.

     

     

    Aaron