我偶然发现了一种有趣的(?)隐藏密码(和其他个人数据)从屏幕到日志文件的常规输出的方法。
在他的书How to make mistakes in Python中,Mike Pirnat建议为敏感字符串实现一个类,并重载其__str__
- 和__repr__
- 方法。
我试验了这个并得到了这个:
class secret(str):
def __init__(self, s):
self.string = s
def __repr__(self):
return "'" + "R"*len(self.string) + "'"
def __str__(self):
return "S" * len(self.string)
def __add__(self, other):
return str.__add__(self.__str__(), other)
def __radd__(self, other):
return str.__add__(other, self.__str__())
def __getslice__(self, i, j):
return ("X"*len(self.string))[i:j]
(我知道使用len
提供了有关要隐藏的内容的信息。它只是用于测试。)
在这种情况下它可以正常工作:
pwd = secret("nothidden")
print("The passwort is " + pwd) # The passwort is SSSSSSSSS
print(pwd + " is the passwort.") # SSSSSSSSS is the password.
print("The passwort is {}.".format(pwd)) # The password is SSSSSSSSS.
print(["The", "passwort", "is", pwd]) # ['The', 'password', 'is', 'RRRRRRRRR']
print(pwd[:]) # XXXXXXXXX
然而,这不起作用:
print(" ".join(["The", "password", "is", pwd])) # The password is nothidden
那么,str.join()如何在内部工作?我需要使用哪种方法来覆盖字符串?
答案 0 :(得分:5)
问题在于您继承自str
,它可能实现__new__
,这意味着即使您避免在类中调用父构造函数,底层C对象仍然会使用它进行初始化。
现在join
可能正在检查它是否有str
子类,并且在C中实现,它直接访问底层C结构,或使用其他str
相关函数绕过__str__
和__repr__
(想一想:如果值是字符串或字符串子类,为什么代码会调用__str__
或__repr__
来获取其值?只是以某种方式访问底层字符数组!)
要解决此问题:不继承str
!不幸的是,这意味着在某些情况下你将无法像字符串一样使用该对象,但这几乎是不可避免的。
可能有效的替代方法是实施__new__
并为str
的{{1}}方法提供不同的值:
__new__
结果是:
class secret(str):
def __new__(cls, initializer):
return super(secret, cls).__new__(cls, 'X'*len(initializer))
def __init__(self, initializer):
self.text = initializer
def __repr__(self):
return "'{}'".format("R"*len(self))
def __str__(self):
return "S"*len(self)
def __add__(self, other):
return str(self) + other
def __radd__(self, other):
return other + str(self)
然而,我没有真正看到它是如何有用的。我的意思是:这个类的目的是避免编程错误,最终显示敏感信息?但是,触发异常会更好,以便您可以识别错误!为此,In [19]: pwd = secret('nothidden')
In [20]: print("The passwort is " + pwd) # The passwort is SSSSSSSSS
...: print(pwd + " is the passwort.") # SSSSSSSSS is the password.
...:
...: print("The passwort is {}.".format(pwd)) # The password is SSSSSSSSS.
...: print(["The", "passwort", "is", pwd]) # ['The', 'password', 'is', 'RRRRRRRRR']
...: print(pwd[:])
The passwort is SSSSSSSSS
SSSSSSSSS is the passwort.
The passwort is SSSSSSSSS.
['The', 'passwort', 'is', 'RRRRRRRRR']
XXXXXXXXX
In [21]: print(" ".join(["The", "password", "is", pwd]))
The password is XXXXXXXXX
和raise NotImplementedError
内的__str__
可能是最好的,而不是默默地提供无用的价值......确保你没有泄漏秘密,但发现错误变得非常困难。