邮件URL中的正则表达式匹配密码

时间:2014-01-17 12:27:15

标签: python regex regex-lookarounds

假设我有这样的SMTP / IMAP / POP3登录网址:

smtp://foobar@example.com:abc@smtp.example.com:465

我想用恒定数量的星号(例如abc)替换密码(在这种情况下为*****),以便将其隐藏起来。

到目前为止我尝试的内容大量使用了外观:

def starPassword(route):
    """
    >>> starPassword("smtp://foobar@example.com:abc@smtp.example.com:465")
    'smtp://foobar@example.com:*****@smtp.example.com:465'
    >>> starPassword("smtp://foobar:abc@smtp.example.com:25")
    'smtp://foobar:*****@smtp.example.com:465'
    """
    # Regex explanation:
    #  (?<=\w+://\w+:) matches the colon before the password without consuming 
    #  ([^@]+) matches the password (TODO use a better match, passwords might contain @! Check escaping)
    #  (?=@[^@]+$) matches the @ after the server, plus the rest of the URL
    return re.sub("(?<=:)([^@]+)(?=@[^@]+$)", "*****", route)
if __name__ == "__main__":
    import doctest
    doctest.testmod()

不幸的是,这个正则表达式有几个问题,包括:

  • 第一个单元测试成功,但第二个不成功,因为协议(smtp://冒号匹配)。我尝试了(?<=\w+://\w+:),但外观需要自定义长度。也许我可以使用这些URL部分并替换为\1*****\2)或类似的东西?
  • 包含@和/或:的密码将无法识别,我甚至不确定它们是如何转义的(这就是我不使用非贪婪标志的原因)

请注意,我不能使用Python3(urlparse模块) - 除非严格需要,否则我不想使用第三方库。

提前感谢我指出了正确的方向。

2 个答案:

答案 0 :(得分:3)

您可以使用urlparse.urlsplit() function(Python 2中也提供); .netloc参数将包含用户名和密码(两者都将被转义为不包含普通:@个字符,请参阅RFC 3986 Section 3.2.1):

import urlparse

def starPassword(route):
    parsed = urlparse.urlsplit(route)
    if '@' not in parsed.netloc:
        return route

    userinfo, _, location = parsed.netloc.partition('@')
    username, _, password = userinfo.partition(':')
    if not password:
        return route

    userinfo = ':'.join([username, '*****'])
    netloc = '@'.join([userinfo, location])
    parsed = parsed._replace(netloc=netloc)
    return urlparse.urlunsplit(parsed)

演示:

>>> starPassword('smtp://foobar%40example.com:abc@smtp.example.com:465')
'smtp://foobar%40example.com:*****@smtp.example.com:465'
>>> starPassword('smtp://foobar:abc@smtp.example.com:25')
'smtp://foobar:*****@smtp.example.com:25'
>>> starPassword('smtp://smtp.example.com:1234')
'smtp://smtp.example.com:1234'
>>> starPassword('smtp://foo@smtp.example.com:42')
'smtp://foo@smtp.example.com:42'

答案 1 :(得分:2)

使用此正则表达式:

(?<=:)([^@:]+)(?=@[^@]+$)

我将:添加到[^@]。因此,此正则表达式将匹配:@之间的字符串,而中间没有任何:@

print( re.sub("(?<=:)([^@:]+)(?=@[^@]+$)", "*****",
              "smtp://foobar:abc@smtp.example.com:25") )

smtp://foobar:*****@smtp.example.com:25