CodeQL 文档

日志注入

ID: py/log-injection
Kind: path-problem
Security severity: 7.8
Severity: error
Precision: medium
Tags:
   - security
   - external/cwe/cwe-117
Query suites:
   - python-security-extended.qls
   - python-security-and-quality.qls

点击查看 CodeQL 仓库中的查询

如果将未经清理的用户输入写入日志条目,恶意用户可能能够伪造新的日志条目。

如果用户提供了一些在显示日志输出时会被解释的字符,则可能会发生伪造。如果日志以纯文本文件形式显示,则恶意用户可以使用换行符来创建多个日志条目的外观。如果日志以 HTML 形式显示,则可以包含任意 HTML 来伪造日志条目。

建议

在记录用户输入之前,应对其进行适当的清理。

如果日志条目是纯文本,则应从用户输入中删除换行符,例如使用 replace(old, new) 或类似方法。还应注意,用户输入在日志条目中应清晰标记,并且恶意用户不能以其他方式造成混淆。

对于将在 HTML 中显示的日志条目,用户输入应在记录之前进行 HTML 编码,以防止伪造和其他形式的 HTML 注入。

示例

在示例中,用户提供的名称使用日志输出函数(logging.infoapp.logger.info 等)记录。在这四种情况下,用户提供的名称没有经过处理。如果恶意用户提供 Guest%0D%0AUser name: Admin 作为参数,则日志条目将被分为两行,第一行是 User name: Guest code>,第二行是 User name: Admin

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc   :Log Injection
"""
from flask import Flask
from flask import request
from django.utils.log import request_logger
import logging

logging.basicConfig(level=logging.DEBUG)

app = Flask(__name__)

@app.route('/bad1')
def bad1():
    name = request.args.get('name')
    app.logger.info('User name: ' + name) # Bad
    return 'bad1'

@app.route('/bad2')
def bad2():
    name = request.args.get('name')
    logging.info('User name: ' + name) # Bad
    return 'bad2'

@app.route('/bad3')
def bad3():
    name = request.args.get('name')
    request_logger.warn('User name: ' + name) # Bad
    return 'bad3'

@app.route('/bad4')
def bad4():
    name = request.args.get('name')
    logtest = logging.getLogger('test')
    logtest.debug('User name: ' + name) # Bad
    return 'bad4'

if __name__ == '__main__':
    app.debug = True
    handler = logging.FileHandler('log')
    app.logger.addHandler(handler)
    app.run()

在一个良好的示例中,程序使用 replace 函数对用户提供的参数进行处理,并将 \r\n\n 替换为空字符。这在一定程度上减少了日志注入漏洞的发生。

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
"""
@Desc   :Log Injection
"""
from flask import Flask
from flask import request
import logging

logging.basicConfig(level=logging.DEBUG)

app = Flask(__name__)

@app.route('/good1')
def good1():
    name = request.args.get('name')
    name = name.replace('\r\n','').replace('\n','')
    logging.info('User name: ' + name) # Good
    return 'good1'

if __name__ == '__main__':
    app.debug = True
    handler = logging.FileHandler('log')
    app.logger.addHandler(handler)
    app.run()

参考资料

  • ©GitHub, Inc.
  • 条款
  • 隐私