首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python程序和regex

Python程序和regex
EN

Stack Overflow用户
提问于 2015-11-16 07:04:31
回答 1查看 107关注 0票数 1

你能帮我做些正则表达式和Python代码吗?最近,我收到了大量的帮助,帮助Python脚本通过一些防火墙日志。我快到了,但还是需要一点帮助。

这里有一些来自防火墙的日志输出:

代码语言:javascript
复制
Nov 11 00:00:09 firewall %ASA-3-710003: TCP access denied by ACL from 1.1.1.1/50624 to internet:2.2.2.2/80
Nov  6 12:42:23 firewall %ASA-4-106023: Deny tcp src inside:3.3.3.3/42059 dst internet:4.4.4.4/389 by access-group "access_out" [0x0, 0x0]

日志可以都有IPv4和IPv6地址。我需要为每一行提取协议(tcp、udp、icmp)、source_ip、destination_ip、destination_port和一个计数器。但是,从上面的两行可以看出,日志输出在这两行中是不同的,例如,在一行中,协议用大写,另一行用小写,IP地址之间也有不同的文本。我能够从其中一行得到正确的输出,但两者都不能。

代码语言:javascript
复制
prot   source                    destination               port   hitcnt
------ ------------------        ------------------        ------ ------------------
tcp    1111:2222:0:abcd::12      2222:3333:0:efab::101     389    180
tcp    2222:3333:0:efab::50      1111:2222:0:abcd::12      389    29
ump    1111:4444:0:1111::2       1111:2222:0:abcd::12      123    4
tcp    1.1.1.1                   3.3.3.3                   23     4
imp    2.2.2.2                   4.4.4.4                          4

python脚本中的代码如下所示(感谢holroy)

代码语言:javascript
复制
import re       #for regular expressions - to match ip's
import sys      #for parsing command line opts
from collections import Counter

DENY_PATTERN = re.compile(r'Deny\s(?P<protocol>.+?)\ssrc.*?:(?P<src>[0-9a-f\.]*)/?.*?\s.*?dst.*?:(?P<dst>[0-9a-f\.]*)((/(?P<dst_port>[0-9]*)\s)|\s)')

LINE_FORMAT='{0:<6.6} {1:<25.25} {2:<25.25} {3:<6.6} {4}'

def process_log_file(log file):
    """Reads through the log_file, and returns a counter based on Deny-lines."""

    # Process file line by line
    with open(logfile, 'r') as data :
        seen = Counter()

        # find all Deny line and append them in a list
        for line in data :
            # If line has 'Deny ' in it, then check it some more
            if 'Deny ' in line :
                seen.update(DENY_PATTERN.findall(line)) 
    return seen

def print_counter(counter):
    """Pretty print the result of the counter."""

    print(LINE_FORMAT.format('prot', 'source', 'destination',  'port', 'hitcnt'))
    print(LINE_FORMAT.format(*tuple(('------------------',) * 5)))
    for (protocol, src, dst, _, _, dst_port), count in counter.most_common():
        print(LINE_FORMAT.format(protocol, src, dst, dst_port, count))

if __name__ == '__main__':
    # if file is specified on command line, parse, else ask for file
    if sys.argv[1:] : 
        print "File: %s" % (sys.argv[1])
        logfile = sys.argv[1]
    else:
        logfile = raw_input("Please enter a file to parse, e.g /var/log/secure: ")

    denial_counter = process_log_file(logfile)
    print_counter(denial_counter)

我尝试过很多正则表达式组合(例如https://regex101.com/r/wC6gS7/2,但无法正确地获得输出)。

另外,如果我尝试在两个正则表达式之间使用一个或(\),我无法在这两个正则表达式中将其称为(?P<protocol>),因为它已经被使用过了。如果我在每个正则表达式中使用DENY_PATTERN和DENY_PATTERN2并像这样调用它,那么它也不起作用:

代码语言:javascript
复制
for line in data :
    if 'Deny ' in line :
        seen.update(DENY_PATTERN.findall(line)) 

for line in data :
    if 'ASA-3 ' in line :
        seen.update(DENY_PATTERN2.findall(line))

提前谢谢你。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-11-16 22:12:56

测试多种模式

要测试多个复杂模式,您需要在数据文件中的唯一循环中添加这些测试。也就是说,您需要以下代码:

代码语言:javascript
复制
    for line in data :

        # If line has 'Deny ' in it, then check it some more
        if 'Deny ' in line:
            seen.update(DENY_PATTERN.findall(line)) 

        # If line has 'ASA-3' in it, then check for the other pattern
        if 'ASA-3' in line:
            seen.update(DENIED_PATTERN.findall(line))

通过这种方式,您可以读取文件一次,并且只有在针对DenyASA-3的基本测试已经发生时,才进行更重的模式匹配。

Regexp评论

在您的regexp中,您有一些额外的组,这是不需要的,并且您对[A-Z].*?进行测试,这是一个大写字符,然后是一个非贪婪的匹配。除非这是您所追求的特定内容,否则最好使用[A-Z]*? (或[A-Za-z]*?)来匹配一系列字符。我冒昧地保存了一些额外的版本,以下面的内容结束(请参阅https://regex101.com/r/wC6gS7/8):

代码语言:javascript
复制
DENIED_PATTERN = re.compile(r'''
    :\s+?                                   # Start with leading white space
    (?P<protocol>[A-Z]*?)                   # Capture "<protocol>" start
    \saccess\s+denied\s+by\s+ACL\s+from\s+  # Matches literal string
    (?P<src>[0-9a-f\.:]*)/.*?:              # Captures "<src>" plus extra text
    (?P<dst>[0-9a-f\.:]*)                   # Captures "<dst>"
    (?:/(?P<dst_port>[0-9]*?))?\s           # Optional captures "/<dst_port>" 
    ''', re.X)

还请注意,使用re.X (或re.VERBOSE)可以极大地提高对模式的理解。使用此标志,您可以使用#包括空格和行尾注释。

使用(?: ... )是一个很好的构造来创建一个组,可以通过添加一个? (如在(?:/(?P<dst_port>[0-9]+))?中)来使其成为可选的。这可以选择性地捕获诸如"/80“之类的内容。但是请注意,由于现在它是可选的,所以我添加了一个\s来满足早期构造的繁琐,否则它可能会被忽略。

对于备用组,使用非捕获组构造通常很有用,就像在(?: ... | ... | ...)中一样。但是请注意,如果与命名捕获组相结合,则名称必须是唯一的。

更改print_counter()

由于捕获组的数量在这两种模式中不同,因此也需要修改打印输出。要区分这两种模式,可以依赖捕获组的计数,也可以在匹配组中引入标识符。也就是说,您可以扩展regexp以包括DenyASA-3

我选择使用捕获组的数量。由于现在捕获组的数量在for循环中有所变化,这与直接展开元组带来了一个小问题,因为您有太多或太少的元素需要解包。解决这一问题的办法是稍微推迟扩张。这将以以下代码结束:

代码语言:javascript
复制
def print_counter(counter):
    """Pretty print the result of the counter."""

    print(LINE_FORMAT.format('prot', 'source', 'destination',  'port', 'hitcnt'))
    print(LINE_FORMAT.format(*tuple(('------------------',) * 5)))
    for re_match, count in counter.most_common():

        re_match_length = len(re_match)

        # Pick elements based on match from DENY_PATTERN        
        if re_match_length == 8:
            protocol, src, src_port, dst, dst_port, icmp_spec, icmp_type, icmp_code =  re_match
            if icmp_code or icmp_type:
                dst_port = '{}, {}'.format(icmp_code, icmp_type)

        # Pick elements based on match from DENIED_PATTERN
        if re_match_length == 4:
            protocol, src, dst, dst_port = re_match


        print(LINE_FORMAT.format(protocol.lower(), src, dst, dst_port, count))

为了美化输出,我添加了一个protocol.lower(),以便它们总是以小写表示。这也可以在相应的if语句中完成,并在输出行之前进行其他需要的调整。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/33730000

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档