What are the internals of Pythons str.join()? (Hiding passwords from output) -
i stumbled upon interesting(?) way hide passwords (and other personal data) general output screen logfiles.
in book how make mistakes in python mike pirnat suggests implement class sensitive strings , overload __str__
- , __repr__
-methods.
i experimented , got this:
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]
(i'm aware using len
provides information content hide. it's testing.)
it works fine in cases:
pwd = secret("nothidden") print("the passwort " + pwd) # passwort sssssssss print(pwd + " passwort.") # sssssssss password. print("the passwort {}.".format(pwd)) # password sssssssss. print(["the", "passwort", "is", pwd]) # ['the', 'password', 'is', 'rrrrrrrrr'] print(pwd[:]) # xxxxxxxxx
however not work:
print(" ".join(["the", "password", "is", pwd])) # password nothidden
so, how str.join() work internally? method have overload obscure string?
the issue inheriting str
, implements __new__
means though avoided calling parent constructor in class, underlying c object still initialized it.
now join
checking if has str
subclass and, being implemented in c, access directly underlying c structure, or uses other str
-related function bypasses __str__
, __repr__
(think it: if value string or string subclass, why code call __str__
or __repr__
obtain value? accesses underlying character array in way!)
to fix this: not inherit str
! unfortunately means not able use object string in situations, that's pretty inevitable.
an alternative may work implement __new__
, feed different value str
's __new__
method:
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)
which results in:
in [19]: pwd = secret('nothidden') in [20]: print("the passwort " + pwd) # passwort sssssssss ...: print(pwd + " passwort.") # sssssssss password. ...: ...: print("the passwort {}.".format(pwd)) # password sssssssss. ...: print(["the", "passwort", "is", pwd]) # ['the', 'password', 'is', 'rrrrrrrrr'] ...: print(pwd[:]) passwort sssssssss sssssssss passwort. passwort sssssssss. ['the', 'passwort', 'is', 'rrrrrrrrr'] xxxxxxxxx in [21]: print(" ".join(["the", "password", "is", pwd])) password xxxxxxxxx
however fail see how useful. mean: purpose of class avoid programming errors end display sensitive information? having exception being triggered better can identify bugs! it's best raise notimplementederror
inside __str__
, __repr__
instead of silently provided useless value... sure don't leak secret finding bugs becomes hard.
Comments
Post a Comment