2016-11-08 71 views
11

我只是偶然發現了一種有趣的(?)方法來將密碼(和其他個人數據)從屏幕上的一般輸出隱藏到日誌文件。什麼是Pythons str.join()的內部? (從輸出中隱藏密碼)

在他的書How to make mistakes in Python Mike Pirnat建議實施一個敏感字符串類並超載它的__str____repr__-方法。

我嘗試了這一點,得到了這一點:(我知道,使用len提供有關內容隱藏信息這只是用於測試。)

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] 

它工作正常在這種情況下:

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()如何在內部工作?我需要重載哪種方法才能遮蔽字符串?

回答

5

問題是您從str繼承,這可能實現__new__這意味着即使您避免在您的類中調用父構造函數,底層C對象仍然使用它初始化。

現在join可能是並檢查它是否有一亞類str,在C中實現,但是直接訪問底層的C結構,或使用一個其他str - 相關函數繞過__str____repr__(想一想:如果?值是一個字符串或字符串的子類,爲什麼會在代碼調用__str____repr__獲得其價值它只是訪問在某種程度上底層字符數組)

要解決這個問題:從str繼承!不幸的是,這意味着在某些情況下你將無法像使用字符串一樣使用該對象,但這幾乎是不可避免的。


可工作的替代方案是實現__new__和飼料一個不同的值,以str__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____repr__之內,而不是默默地提供一個無用的價值......確保你不泄露祕密,但是發現錯誤變得非常困難。

+1

這類作品。 ''print(「」.join([「The」,「password」,「is」,pwd]))'會產生一個回溯,但是在'str()'中包裝'pwd'將產生所需的輸出。當然,這也會修復原始版本。我想這是一個更安全一點,產生一個追蹤而不是打印隱藏的字符串。 – TigerhawkT3

+0

@ TigerhawkT3我提供了一個按預期工作的實現,並添加了一個關於爲什麼這不是非常有用的說明......如果你不想顯示某些東西,我只是*不*執行其中的一些方法或在被調用時引發異常。 – Bakuriu

+0

這不是關於編程*錯誤*,而是關於記錄REST查詢或類似內容時的便利性,所以您不必每次都自己模糊它。 對於最重要的部分,這是好奇心。 但你可能是對的,有更好的方法來避免打印合理的數據。 –