Some important Cisco ASA firewall details I and others have learned and shared over the years:

Don’t use “security-level” as your method of security. In the long term at best “security-level” will cause you to block traffic you didn’t expect, at worst, it will allow traffic you didn’t want. Why? Well…

If you add an ACL on the “in” side of any interface (that is “into the ASA”), once it’s in the ASA, the security level doesn’t matter anymore. It’s very easy to forget this. However you can protect yourself by…

Always add “out” rules. Any “in” rules should be matched by “out” rules on the final destination interface. This is insurance in case you missed or were overly broad on your “in” rules.

Configure all of the interfaces to the same “security-level”. If you enable “same-security-traffic permit inter-interface” be careful as it allows traffic to flow to other same security levels without ACLs. You don’t want traffic to flow when you haven’t allowed it explicitly. The only exception to using different security levels might be the “outside” interface, which you may want to set to “security-level 0”. However, assuming “outside” is the Internet, ideally you want to be explicit there too. Otherwise you’re potentially setting yourself up for easy, unlogged, data exfiltration (among other things).

Remember that the ASA is a stateful firewall. If you establish some sort of connection out of an interface, the firewall should see that the return traffic belongs to the conversation and allow it through regardless. For the most part you don’t need to explicitly create return rules (or use the old IOS “established” trick).

If you’re trying to turn up a firewall on a network that existed, but was never firewalled before and you are having difficult categorizing the existing traffic, place the rules that you know are correct into the ASA, then add a “permit ip any any log” entry at the end. This will send logging of what fell to the wildcard rule to your syslog server, which you can then evaluate later. Once analyzed and missing rules in place, turn it to a “deny ip any any” and you’re done. Remember you can also do packet capture on the ASA as well.

Never trust a 3rd party. If they are coming into your network and saying they are properly filtering traffic toward you, filter them again anyway. First, their error could be your exploit, second you can’t assume their firewalls aren’t going to get hacked. Protect your network like it was your own child.

Beware of mixing ASA “access-list”s and ASA VPNs on the same firewall. Unless you want to enter “filter” hell, which generally you can only apply usefully in one direction, turn off VPN bypass with “no sysopt connection permit-vpn”. If you don’t do this YOUR VPN TRAFFIC BYPASSES ALL “access-list” RULES! Note that once you disable “VPN bypass”, your VPN traffic will appear to come from the “in” of the interface it initially arrived at. Since that’s usually “outside” and the Internet, you can have a seemingly less-than pretty mix of private addressing and public addressing to deal with on your Internet interface. This can make it cleaner to get a dedicated ASA for VPN and hang it off an arm of your firewall ASA.

The most critical thing with firewalls is don’t be lazy. Take the time to do the configuration and rules needed. It takes extra effort up front, but a failure is far more expensive.

Sometimes you need the service tag or model off a Dell server that isn’t in your possession. You can either find some feet on the street to do it or as it turns out, with Linux, you can use “dmidecode”:

Although EMV cards will provide greater security than traditional magnetic strip cards, they are still vulnerable to fraud. EMV cards can be counterfeited using stolen card data obtained from the black market. Additionally, the data on the magnetic strip of an EMV card can still be stolen if the PoS terminal is infected with data-capturing malware. Further, the EMV chip will likely not stop stolen or counterfeit credit cards from being used for online or telephone purchases where the card is not physically seen by the merchant and where the EMV chip is not used to transmit transaction data.

You can look at EMV two ways – a good start, or a lot of effort and money, in retrospect, potentially put toward the wrong solution. Yes, it is better than the status quo in the states, but it doesn’t so much as solve the issue as shift it. The fact is, memory scrapers will still be able to get the vast majority of information they need to create counterfeit cards for use in locations or merchants who have yet to embrace EMV, or alternatively, use the cards online where EMV is inapplicable.

While there is no panacea – the hackers will find a way, perhaps a better investment would be driving merchants to P2PE and E2EE solutions (or hybrids). That too would be expensive for merchants to implement, but at least addresses most of the major concerns in today’s security environment.

UPDATE: The above has hit the media, but seems to have disappeared from the FBI site.

UPDATE 2: While there is nothing official – some outlets have noticed the disappearance. The suspected cause was a concern from the banking industry:

“We saw the PSA yesterday and spoke to the FBI after we saw it and we thought it was not really reflective of the U.S. marketplace and thought there would have been some level of confusion with the use of PIN.”

I would have to agree, while it does not make a ton of sense that the PIN portion wasn’t implemented (which would have stopped physically stolen cards), the real concern is not in the PIN or lack thereof, but rather that the full track data is still transmitted by default in the clear.

Although EMV cards provide greater security than traditional magnetic strip cards, an EMV chip does not stop lost and stolen cards from being used in stores, or for online or telephone purchases when the chip is not physically provided to the merchant, referred to as a card-not-present transaction. Additionally, the data on the magnetic strip of an EMV card can still be stolen if the merchant has not upgraded to an EMV terminal and it becomes infected with data-capturing malware. Consumers are urged to use the EMV feature of their new card wherever merchants accept it to limit the exposure of their sensitive payment data.

The language “upgraded to an EMV terminal” either is confused or confusing. Just because a “terminal” (PIN Pad?) is EMV capable, does not mean the transaction is encrypted in the terminal prior to transmission to the POS, nor does it mean that the POS does not decrypt the transaction. If it is not encrypted or it is decrypted at the POS, the POS can be used or possible memory scraping (“data-capturing malware”). Again, the PIN Pad and merchant payment infrastructure needs to support P2PE or E2EE solutions for that kind of protection.

Note that even if it is encrypted at “terminal” and not decrypted at the POS, if it is decrypted anywhere within the merchant’s network, that could be a location for “data-capturing malware” to be installed. By using P2PE or E2EE, that risk can essentially be pushed out of the merchant and down to issuers or processors.

As always, the opinions above are my own, and do not necessarily represent my employer’s.

I wouldn’t mind the “People Pane”, except that in our organization is shows nothing useful. Moreover for a reason I cannot fathom, it always gets opened up, taking enormous reading real estate. So from this (Outlook 2010 at least):

Working in a non-homogenous (that is, heterogeneous OS) environment where Python 2.x vs. Python 3.x is not guaranteed, the lack of backwards (or forwards) compatibility is problematic. If nothing else it erodes trust in the language – will Python 4.x inflict similar pain? Should I be looking to yet another language that isn’t so willing to shoot itself in the foot?

Not all environments, even if they should be, are Puppet-perfect and portability is still a major requirement.

This is to some extent what happens when languages become religion and “purity” to ideological dogma becomes more important than functionality. While I have to praise the desire for perfection, sometimes the perfect really is the enemy of the good.

All that said, as Python becomes more “native” to my mentality, maybe I’ll change my mind. I’ve found the transition to other languages, OSes, etc. offensive to my sensibilities only to later become “assimilated to the Borg” as it were.

Most LDAP servers can be set to return an unlimited number of entries on an LDAP search, however depending on the size of the LDAP database/directory this can possibly exceed your memory. Moreover if you want to write portable code, you probably should not depend on the LDAP server being able to return unlimited entries. For instance, AD’s LDAP generally defaults to 1,000 entries maximum.

Because using LDAP paging isn’t very difficult there’s not a lot of reason to not use it. Adding paging only marginally reduces performance, while certainly putting less stress on the LDAP server(s). Personally I recommend you use it on a general basis, even where not strictly necessary.

Python’s LDAP supports paging, though it isn’t well documented. I found two examples this one and this one. Both had their pluses, but neither explained what was going on too much. I melded them together, added comments, and streamlined a bit. Hopefully this will help you get the mojo…

For users of Python LDAP 2.4, you should check out of the comment by Ilya Rumyantsev which gives a forward/backward compatible set of code snippets since the API has changed a bit. Many thanks to Ilya for the update.

UPDATE 3:

Below I took Ilya’s updates and merged them in with some minor enhancements to compare the Python LDAP version on the fly. My next stop is to take this and convert it to a generator function, which would be more ideal than using a callback. The issue with going to a generator is handling the errors, which means throwing exceptions in some sane fashion…

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

#! /usr/bin/python

import sys

import ldap

from ldap.controls import SimplePagedResultsControl

from distutils.version import LooseVersion

# Check if we're using the Python "ldap" 2.4 or greater API

LDAP24API=LooseVersion(ldap.__version__)&gt;=LooseVersion('2.4')

# If you're talking to LDAP, you should be using LDAPS for security!

LDAPSERVER='ldaps://ldap.somecompany.com'

BASEDN='cn=users,dc=somecompany,dc=com'

LDAPUSER='uid=someuser,dc=somecompany,dc=com'

LDAPPASSWORD='somepassword'

PAGESIZE=1000

ATTRLIST=['uid','shadowLastChange','shadowMax','shadowExpire']

SEARCHFILTER='uid=*'

def create_controls(pagesize):

"""Create an LDAP control with a page size of "pagesize"."""

# Initialize the LDAP controls for paging. Note that we pass ''

# for the cookie because on first iteration, it starts out empty.

# Note you may want to set "criticality=True" if you must have

# paging.

ifLDAP24API:

returnSimplePagedResultsControl(criticality=False,size=pagesize,

cookie='')

else:

returnSimplePagedResultsControl(ldap.LDAP_CONTROL_PAGE_OID,False,

(pagesize,''))

def get_pctrls(serverctrls):

"""Lookup an LDAP paged control object from the returned controls."""

# Look through the returned controls and find the page controls.

# This will also have our returned cookie which we need to make

# the next search request.

ifLDAP24API:

return[cforcinserverctrls

ifc.controlType==SimplePagedResultsControl.controlType]

else:

return[cforcinserverctrls

ifc.controlType==ldap.LDAP_CONTROL_PAGE_OID]

def set_cookie(lc_object,pctrls,pagesize):

"""Push latest cookie back into the page control."""

ifLDAP24API:

cookie=pctrls[0].cookie

lc_object.cookie=cookie

returncookie

else:

est,cookie=pctrls[0].controlValue

lc_object.controlValue=(pagesize,cookie)

returncookie

# This is essentially a placeholder callback function. You would do your real

# work inside of this. Really this should be all abstracted into a generator...

def process_entry(dn,attrs):

"""Process an entry. The two arguments passed are the DN and

a dictionary of attributes."""

print dn,attrs

# Ignore server side certificate errors (assumes using LDAPS and

# self-signed cert). Not necessary if not LDAPS or it's signed by

# a real CA.

ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_ALLOW)

# Don't follow referrals

ldap.set_option(ldap.OPT_REFERRALS,0)

l=ldap.initialize(LDAPSERVER)

l.protocol_version=3# Paged results only apply to LDAP v3

try:

l.simple_bind_s(LDAPUSER,LDAPPASSWORD)

except ldap.LDAPError ase:

exit('LDAP bind failed: %s'%e)

# Create the page control to work from

lc=create_controls(PAGESIZE)

# Do searches until we run out of "pages" to get from

# the LDAP server.

whileTrue:

# Send search request

try:

# If you leave out the ATTRLIST it'll return all attributes

# which you have permissions to access. You may want to adjust

# the scope level as well (perhaps "ldap.SCOPE_SUBTREE", but

# it can reduce performance if you don't need it).

msgid=l.search_ext(BASEDN,ldap.SCOPE_ONELEVEL,SEARCHFILTER,

ATTRLIST,serverctrls=[lc])

except ldap.LDAPError ase:

sys.exit('LDAP search failed: %s'%e)

# Pull the results from the search request

try:

rtype,rdata,rmsgid,serverctrls=l.result3(msgid)

except ldap.LDAPError ase:

sys.exit('Could not pull LDAP results: %s'%e)

# Each "rdata" is a tuple of the form (dn, attrs), where dn is

# a string containing the DN (distinguished name) of the entry,

# and attrs is a dictionary containing the attributes associated

# with the entry. The keys of attrs are strings, and the associated

# values are lists of strings.

fordn,attrs inrdata:

process_entry(dn,attrs)

# Get cookie for next request

pctrls=get_pctrls(serverctrls)

ifnotpctrls:

print&gt;&gt;sys.stderr,'Warning: Server ignores RFC 2696 control.'

break

# Ok, we did find the page control, yank the cookie from it and

# insert it into the control for our next search. If however there

# is no cookie, we are done!

cookie=set_cookie(lc,pctrls,PAGESIZE)

ifnotcookie:

break

# Clean up

l.unbind()

# Done!

sys.exit(0)

UPDATE 4:

It turns out that the Python “ldap” module does not follow “StrictVersion” versioning in it’s “__version__” string. I have updated the “UPDATE 3” code to use “LooseVersion” comparisons.

UPDATE 5:

I updated the above code to default to “criticality=False” for the paging control. If the LDAP service doesn’t support paging, it will yield a potentially confusing “Critical extension is unavailable” error.

Note I need to ultimately fix the exception handling as for whatever reason the exception object passed back doesn’t have a reasonable “__str__()” method and the message is left in the “desc” key.

Recent Posts

My Resume

I've worked in professionally in the systems, networking, security, and programming arenas for the last 25+ years, much of it in leadership or management roles. I am equally comfortable in highly technical “hands on” scenarios as with interacting with executives to drive projects forward.

My resume and other information about my experience can be found here and here.

Bookshelf

Caveat Emptor

The views expressed here are strictly my own and do not represent those of my employer, its officers, nor any other organization.