将来自不受信任来源的信息作为 python 异常消息放置是否安全?

Is it safe to put information from an untrusted source as python exception message?

有没有以下危险的方式?例如,如果有人将此异常打印到 stdout?

,代码能否执行?
def somefunc(val_from_untrusted_source):
    if val_from_untrusted_source == 'something_we_except':
        # do something useful
        pass
    else:
        raise RuntimeException('unknown input: {}'.format(val_from_untrusted_source))

据我所知,没有什么危险是不可能发生的。最不愉快的后果:

  1. (如果 Python 假定为 2)如果 val_from_untrusted_source 不是 ASCII,则 UnicodeEncodeError 将在尝试引发 RuntimeException
  2. 时出现
  3. 如果 val_from_untrusted_source 包含 '\r' 并且有人将此异常写入日志 - 当前日志行的开头可能会被覆盖

永远不要那样!

日志消息旨在通知有关处理的信息,应该安全地传送,以便在出现问题时找到发生的事情的证据。这里没有进行任何控制,所以这里有一些可能的问题:

  • str(val_from_untrusted_source) 本身可能引发异常,其中 UnicodeEncodeError 用于在 Python 2 中包含非 ASCII 字符的 unicode 字符串,或 UnicodeDecodeError 用于包含非 ASCII 的字节字符串Python 3 中的字符。这还包括特制对象,异常引发 __str__ 方法
  • str(val_from_untrusted_source) 可能是一个很长的字符串。它包括长字节或 unicode 字符串,还包括特制的微小对象:

    class DONT:
        def __init__(self, size, pattern):
            self.pattern = pattern
            self.size = size
        def __str__(self):
            return self.pattern * self.size
    

    它们可能导致日志文件耗尽到磁盘 space

  • 可以使用永无止境的 __str__ 方法或试图耗尽内存的方法特制对象。想象一下上面 DONT...
  • 的细微变化

所有这些都是在代码不受控制的更糟糕的用例中可能发生的情况。在更实际的使用场景中,val_from_untrusted_source 可能是一个字符串。在那种情况下,限制其大小并处理 UnicodeError exception:

就足够了
if val_from_untrusted_source == 'something_we_except':
    # do something useful
    pass
else:
    try:
        txt = str(val_from_untrusted_source)
    except UnicodeEncodeError:                # unicode string on Python 2
        txt = val_from_untrusted_source.encode(error = 'replace')
    except UnicodeDecodeError:                # byte string on Python 3
        txt = val_from_untrusted_source.decode(error = 'replace')
    except Exception:                         # quite weird but once we are there...
        txt = "object non convertible to string"
    if len(text) > 47):     # limit length to 50
        txt = txt[:47] + '...'
    raise RuntimeException('unknown input: {}'.format(txt))