本规范以PEP8为基础,参考华为Python通用编码规范、安全编程规范,并结合业界共识整理而成,参与MindSpore社区开发需要首先遵循本规范内容(与PEP8冲突部分),其余遵循PEP8规范;
如果对规则异议,建议提交issue并说明理由,经MindSpore社区运营团队评审接纳后可修改生效;
MindSpore开源社区
class _Foo:
_instance = None
pass
def _func_example(path):
pass
如果超过120个字符,请选择合理的方式进行换行。
所有python文件,均需包含如下版权声明:
# Copyright 2019 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
Add notes.
"""
import xxx
关于版权说明,应注意:
2020年新建的文件,应该是Copyright 2020 Huawei Technologies Co., Ltd
2019年创建年份,2020年修改年份,应该是Copyright 2019-2020 Huawei Technologies Co., Ltd
class
和 def
的注释格式相同,采用业界通用的python注释语法,写在声明下方并缩进,所有的 class
和 def
都需要写注释,模块内部的类和方法可以只写一条简介。当文件路径来自外部数据时,需要先将文件路径规范化,如果没有作规范化处理,攻击者就有机会通过恶意构造文件路径进行文件的越权访问:
例如,攻击者可以构造“../../../etc/passwd”的方式进行任意文件访问。
在linux下,使用realpath函数,在windows下,使用PathCanonicalize函数进行文件路径的规范化。
【错误代码示例】
以下代码从外部获取到文件名称,拼接成文件路径后,直接对文件内容进行读取,导致攻击者可以读取到任意文件的内容:
错误代码示例
【正确代码示例】
正确的做法是,对路径进行规范化后,再判断路径是否是本程序所认为的合法的路径:
正确代码示例
【例外】
运行于控制台的命令行程序,通过控制台手工输入文件路径,可以作为本建议例外。
使用未经校验的不可信输入作为系统命令的参数或命令的一部分,可能导致命令注入漏洞。对于命令注入漏洞,命令将会以与Python应用程序相同的特权级别执行,它向攻击者提供了类似系统shell的功能。在Python中,os.system 或 os.popen 经常被用来调用一个新的进程,如果被执行的命令来自于外部输入,则可能会产生命令和参数注入。
执行命令的时候,请注意以下几点:
【错误代码示例1】
攻击者可以通过找到环境变量APPHOME对应的值,并且在相应目录下放置常量INITCMD对应的攻击程序,达到执
行的效果:
home = os.getenv('APPHOME')
cmd = os.path.join(home, INITCMD)
os.system(cmd)
【错误代码示例2】
没有校验属性 backuptype 的值,这个是用户输入的,攻击者可能进行攻击,例如:用户输入的是:" && del
c:\dbms\. ":
# 值来自用户配置
btype = req.field('backuptype')
cmd = "cmd.exe /K \"c:\\util\\rmanDB.bat " + btype + "&&c:\\util\\cleanup.bat\""
os.system(cmd)
【错误代码示例3】
没有校验属性 backuptype 的值,这个是用户输入的,攻击者可能进行攻击,例如:用户输入的是:" && del
c:\dbms\. ":
import os
import sys
try:
print(os.system("ls " + sys.argv[1]))
except Exception as ex:
print('exception:', ex)
攻击者可以通过以下命令来利用这个漏洞程序:
python test.py ". && echo bad"
实际将会执行两个命令:
ls .
echo bad
【正确代码示例】
避免使用 os.system,可以使用标准的 API 替代运行系统命令来完成任务:
import os
import sys
try:
print(os.listdir(sys.argv[1]))
except Exception as ex:
print(ex)
每一个except 块都应该确保程序只会在继续有效的情况下才会继续运行下去。except 块必须要么从异常情况中恢复,要么重新抛出适合当前catch块上下文的另一个异常以允许最邻近的外层try-except 语句块来进行恢复工作。
【正确代码示例】
正确的做法是,避免使用 os.system,可以使用标准的 API 替代运行系统命令来完成任务:
validFlag = False
while not validFlag:
try:
# If requested file does not exist, throws FileNotFoundError
# If requested file exists, sets validFlag to true
validFlag = True
except FileNotFoundError:
import traceback
traceback.print_exc()
【例外情况】:
使用try…except…结构对代码作保护时,如果代码执行出现了异常,为了能够可靠地关闭操作对象,需要使用finally…结构确保释放操作对象。
【正确代码示例】
handle = open(r"/tmp/sample_data.txt") # May raise IOError
try:
data = handle.read() # May raise UnicodeDecodeError
except UnicodeDecodeError as decode_error:
print(decode_error)
finally:
handle.close() # Always run after try:
在异常这方面, Python非常宽容,“except:”语句真的会捕获包括Python语法错误在内的任何错误。使用“except:”很容易隐藏真正的bug,我们在使用try…except…结构对代码作保护时,应该明确期望处理的异常。Exception类是大多数运行时异常的基类,一般也应当避免在except语句中使用。通常,try只应当包含必须要在当前位置处理异常的语句,except只捕获必须处理的异常。比如对于打开文件的代码,try应当只包含open语句,except只捕获FileNotFoundError异常。对于其他预料外的异常,则让上层函数捕获,或者透传到程序外部来充分暴露问题。
【错误代码示例】
如下代码可能抛出两种异常,使用“except:”语句进行统一处理时,如果是open执行异常,将在“except:”语句之后handle无效的情况下调用close,报错handle未定义。
try:
handle = open(r"/tmp/sample_data.txt") # May raise IOError
data = handle.read() # May raise UnicodeDecodeError
except:
handle.close()
【正确代码示例】
try:
handle = open(r"/tmp/sample_data.txt") # May raise IOError
try:
data = handle.read() # May raise UnicodeDecodeError
except UnicodeDecodeError as decode_error:
print(decode_error)
finally:
handle.close()
except(FileNotFoundError, IOError) as file_open_except:
print(file_open_except)
raise关键字单独使用只能出现在try-except语句中,重新抛出except抓住的异常。
【错误代码示例】
a = 1
if a==1:
raise
【正确代码示例1】raise一个Exception或自定义的Exception
a = 1
if a==1:
raise Exception
【正确代码示例2】在try-except语句中使用
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except IOError as e:
print("I/O error({0}): {1}".format(e.errno, e.strerror))
except ValueError:
print("Could not convert data to an integer.")
except Exception:
print("Unexpected error:", sys.exc_info()[0])
raise
Python产生随机数的功能在random模块中实现,实现了各种分布的伪随机数生成器。产生的随机数可
以是均匀分布,高斯分布,对数正态分布,负指数分布以及alpha,beta分布,但是这些随机数都是伪随机数,不
能应用于安全加密目的的应用中。
请使用/dev/random生成安全随机数,或者使用在python 3.6版本官方引入的secrets模块生成安全随机数。
【错误代码示例】
import random
# 伪随机数
func = random.SystemRandom()
print(func.random())
print(func.randint(0, 10))
【正确代码示例】
import platform
# 长度请参见密码算法规范,不同场景要求长度不一样
randLength = 16
if platform.system() == 'Linux':
with open("/dev/random", 'rb') as file:
sr = file.read(randLength)
print(sr)
assert只应在研发过程中内部测试时使用,出现了AssertionError异常说明存在软件设计或者编码上的错误,应
当修改软件予以解决。在对外发布的生产版本中禁止包含assert功能。