You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

coding_guild_python_zh_cn.md 15 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <!-- TOC -->
  2. - [说明](#说明)
  3. - [适用范围](#适用范围)
  4. - [1. 代码风格](#1-代码风格)
  5. - [1.1 命名](#11-命名)
  6. - [1.2 格式](#12-格式)
  7. - [1.3 注释](#13-注释)
  8. - [1.4 日志](#14-日志)
  9. - [2. 通用编码](#2-通用编码)
  10. - [2.1 接口声明](#21-接口声明)
  11. - [2.2 数据校验](#22-数据校验)
  12. - [2.3 异常行为](#23-异常行为)
  13. - [2.4 序列化和反序列化](#24-序列化和反序列化)
  14. <!-- /TOC -->
  15. ## 说明
  16. 本规范以[PEP8](https://www.python.org/dev/peps/pep-0008/)为基础,参考华为Python通用编码规范、安全编程规范,并结合业界共识整理而成,参与MindSpore社区开发需要首先遵循本规范内容(与PEP8冲突部分),其余遵循PEP8规范;
  17. 如果对规则异议,建议提交issue并说明理由,经MindSpore社区运营团队评审接纳后可修改生效;
  18. ## 适用范围
  19. MindSpore开源社区
  20. ------------------------
  21. ### 1. 代码风格
  22. #### 1.1 命名
  23. <font size=3>**规则 1.1.1 包(Package)、模块(Module)名使用意义完整的英文描述,采用小写加下划线(lower_with_under)的风格命名。**</font>
  24. <font size=3>**规则 1.1.2 类名:使用驼峰格式,首字母大写,私有类下划线前缀。**</font>
  25. ```python
  26. class _Foo:
  27. _instance = None
  28. pass
  29. ```
  30. <font size=3>**规则 1.1.3 函数名、变量名:小写,多个单词下划线分割。**</font>
  31. ```python
  32. def _func_example(path):
  33. pass
  34. ```
  35. <font size=3>**建议 1.1.4 除迭代器与计数器除外,禁止使用单字符命名。**</font>
  36. #### 1.2 格式
  37. <font size=3>**规则 1.2.1 每行字符数不要超过 120 个。**</font>
  38. 如果超过120个字符,请选择合理的方式进行换行。
  39. <font size=3>**规则 1.2.2 使用空格进行缩进,每次缩进4个空格,禁止tab缩进。**</font>
  40. <font size=3>**规则 1.2.3 import顺序:标准库、第三方、自定义模块。**</font>
  41. <font size=3>**规则 1.2.4 返回语句和条件语句中不使用括号。**</font>
  42. <font size=3>**规则 1.2.5 模块级函数和类之间双空行,类成员函数之间一空行,注释与代码间按需添加空行,原则上不超过两空行。**</font>
  43. <font size=3>**规则 1.2.6 无效或冗余代码直接删除,不要以注释、TODO等方式保留在代码中,建议提issue记录。**</font>
  44. #### 1.3 注释
  45. <font size=3>**规则 1.3.1 文件头注释必须包含版权声明。**</font>
  46. 所有python文件,均需包含如下版权声明:
  47. ```python
  48. # Copyright 2019 Huawei Technologies Co., Ltd
  49. #
  50. # Licensed under the Apache License, Version 2.0 (the "License");
  51. # you may not use this file except in compliance with the License.
  52. # You may obtain a copy of the License at
  53. #
  54. # http://www.apache.org/licenses/LICENSE-2.0
  55. #
  56. # Unless required by applicable law or agreed to in writing, software
  57. # distributed under the License is distributed on an "AS IS" BASIS,
  58. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  59. # See the License for the specific language governing permissions and
  60. # limitations under the License.
  61. # ============================================================================
  62. """
  63. Add notes.
  64. """
  65. import xxx
  66. ```
  67. > 关于版权说明,应注意:
  68. > 2020年新建的文件,应该是`Copyright 2020 Huawei Technologies Co., Ltd`
  69. > 2019年创建年份,2020年修改年份,应该是`Copyright 2019-2020 Huawei Technologies Co., Ltd`
  70. <font size=3>**规则 1.3.2 对外的类、方法、算子、Cell注释格式。**</font>
  71. - `class` 和 `def` 的注释格式相同,采用业界通用的python注释语法,写在声明下方并缩进,所有的 `class` 和 `def` 都需要写注释,模块内部的类和方法可以只写一条简介。
  72. - 注释格式详见[MindSpore注释规范](https://gitee.com/mindspore/community/blob/master/security/comments_specification_zh_cn.md)。
  73. <font size=3>**规则 1.3.3 不允许通过注释屏蔽pylint告警。**</font>
  74. #### 1.4 日志
  75. <font size=3>**规则 1.4.1 异常日志文本首字母大写。**</font>
  76. <font size=3>**规则 1.4.2 日志文本中变量名必须使用单引号注明。**</font>
  77. ### 2. 通用编码
  78. #### 2.1 接口声明
  79. <font size=3>**规则 2.1.1 用户接口在文件的__all__中说明,__all__摆放在import与代码之间。**</font>
  80. <font size=3>**规则 2.1.2 当前文件使用的非对外方法命名采用下划线前缀,内部跨模块使用的方法无需下划线前缀,用户接口在__all__中声明。**</font>
  81. #### 2.2 数据校验
  82. <font size=3>**规则 2.2.1 对所有外部数据进行合法性检查,包括但不限于:函数入参、外部输入命名行、文件格式,文件大小、环境变量、用户数据等。**</font>
  83. <font size=3>**建议 2.2.2 必须对文件路径进行规范化后再使用。**</font>
  84. 当文件路径来自外部数据时,需要先将文件路径规范化,如果没有作规范化处理,攻击者就有机会通过恶意构造文件路径进行文件的越权访问:
  85. 例如,攻击者可以构造“../../../etc/passwd”的方式进行任意文件访问。
  86. 在linux下,使用realpath函数,在windows下,使用PathCanonicalize函数进行文件路径的规范化。
  87. 【错误代码示例】
  88. 以下代码从外部获取到文件名称,拼接成文件路径后,直接对文件内容进行读取,导致攻击者可以读取到任意文件的内容:
  89. ```python
  90. 错误代码示例
  91. ```
  92. 【正确代码示例】
  93. 正确的做法是,对路径进行规范化后,再判断路径是否是本程序所认为的合法的路径:
  94. ```python
  95. 正确代码示例
  96. ```
  97. 【例外】
  98. 运行于控制台的命令行程序,通过控制台手工输入文件路径,可以作为本建议例外。
  99. <font size=3>**规则 2.2.3 禁止调用OS命令解析器执行命令或运行程序。**</font>
  100. 使用未经校验的不可信输入作为系统命令的参数或命令的一部分,可能导致命令注入漏洞。对于命令注入漏洞,命令将会以与Python应用程序相同的特权级别执行,它向攻击者提供了类似系统shell的功能。在Python中,os.system 或 os.popen 经常被用来调用一个新的进程,如果被执行的命令来自于外部输入,则可能会产生命令和参数注入。
  101. 执行命令的时候,请注意以下几点:
  102. 1. 命令执行的字符串不要去拼接输入的参数,如果必须拼接时,要对输入参数进行白名单过滤。
  103. 2. 对传入的参数要做类型校验,例如:整数数据,可以对数据进行整数强制转换。
  104. 3. 保证格式化字符串的正确性,例如:int类型参数的拼接,对于参数要用%d,不能用%s。
  105. 【错误代码示例1】
  106. 攻击者可以通过找到环境变量APPHOME对应的值,并且在相应目录下放置常量INITCMD对应的攻击程序,达到执
  107. 行的效果:
  108. ```python
  109. home = os.getenv('APPHOME')
  110. cmd = os.path.join(home, INITCMD)
  111. os.system(cmd)
  112. ```
  113. 【错误代码示例2】
  114. 没有校验属性 backuptype 的值,这个是用户输入的,攻击者可能进行攻击,例如:用户输入的是:" && del
  115. c:\\dbms\\*.* ":
  116. ```python
  117. # 值来自用户配置
  118. btype = req.field('backuptype')
  119. cmd = "cmd.exe /K \"c:\\util\\rmanDB.bat " + btype + "&&c:\\util\\cleanup.bat\""
  120. os.system(cmd)
  121. ```
  122. 【错误代码示例3】
  123. 没有校验属性 backuptype 的值,这个是用户输入的,攻击者可能进行攻击,例如:用户输入的是:" && del
  124. c:\\dbms\\*.* ":
  125. ```python
  126. import os
  127. import sys
  128. try:
  129. print(os.system("ls " + sys.argv[1]))
  130. except Exception as ex:
  131. print('exception:', ex)
  132. ```
  133. 攻击者可以通过以下命令来利用这个漏洞程序:
  134. ```python
  135. python test.py ". && echo bad"
  136. ```
  137. 实际将会执行两个命令:
  138. ```python
  139. ls .
  140. echo bad
  141. ```
  142. 【正确代码示例】
  143. 避免使用 os.system,可以使用标准的 API 替代运行系统命令来完成任务:
  144. ```python
  145. import os
  146. import sys
  147. try:
  148. print(os.listdir(sys.argv[1]))
  149. except Exception as ex:
  150. print(ex)
  151. ```
  152. #### 2.3 异常行为
  153. <font size=3>**规则 2.3.1 异常必须被妥当处理,禁止抑制或者忽略已检查异常。**</font>
  154. 每一个except 块都应该确保程序只会在继续有效的情况下才会继续运行下去。except 块必须要么从异常情况中恢复,要么重新抛出适合当前catch块上下文的另一个异常以允许最邻近的外层try-except 语句块来进行恢复工作。
  155. 【正确代码示例】
  156. 正确的做法是,避免使用 os.system,可以使用标准的 API 替代运行系统命令来完成任务:
  157. ```python
  158. validFlag = False
  159. while not validFlag:
  160. try:
  161. # If requested file does not exist, throws FileNotFoundError
  162. # If requested file exists, sets validFlag to true
  163. validFlag = True
  164. except FileNotFoundError:
  165. import traceback
  166. traceback.print_exc()
  167. ```
  168. 【例外情况】:
  169. 1. 在资源释放失败不会影响程序后续行为的情况下,释放资源时发生的异常可以被抑制。释放资源的例子包括关闭文件、网络套接字、线程等等。这些资源通常是在except或者fianlly块中被释放,并且在后续的程序运行中都不会再被使用。因此,除非资源被耗尽,否则不会有其他途径使得这些异常会影响程序后续的行为。在充分处理了资源耗尽问题的情况下,只需对异常进行净化和记录日志(以备日后改进)就足够了;在这种情况下没必要做其他额外的错误处理。
  170. 2. 如果在特定的抽象层次上不可能从异常情况中恢复过来,则在那个层级的代码就不用处理这个异常,而是应该抛出一个合适的异常,让更高层次的代码去捕获处理,并尝试恢复。对于这种情况,最通常的实现方法是省略掉catch语句块,允许异常被广播出去。
  171. <font size=3>**规则 2.3.2 使用try…except…结构对代码作保护时,需要在异常后使用finally…结构保证操作对象的释放。**</font>
  172. 使用try…except…结构对代码作保护时,如果代码执行出现了异常,为了能够可靠地关闭操作对象,需要使用finally…结构确保释放操作对象。
  173. 【正确代码示例】
  174. ```python
  175. handle = open(r"/tmp/sample_data.txt") # May raise IOError
  176. try:
  177. data = handle.read() # May raise UnicodeDecodeError
  178. except UnicodeDecodeError as decode_error:
  179. print(decode_error)
  180. finally:
  181. handle.close() # Always run after try:
  182. ```
  183. <font size=3>**规则 2.3.3 不要使用“except:”语句来捕获所有异常。**</font>
  184. 在异常这方面, Python非常宽容,“except:”语句真的会捕获包括Python语法错误在内的任何错误。使用“except:”很容易隐藏真正的bug,我们在使用try…except…结构对代码作保护时,应该明确期望处理的异常。Exception类是大多数运行时异常的基类,一般也应当避免在except语句中使用。通常,try只应当包含必须要在当前位置处理异常的语句,except只捕获必须处理的异常。比如对于打开文件的代码,try应当只包含open语句,except只捕获FileNotFoundError异常。对于其他预料外的异常,则让上层函数捕获,或者透传到程序外部来充分暴露问题。
  185. 【错误代码示例】
  186. 如下代码可能抛出两种异常,使用“except:”语句进行统一处理时,如果是open执行异常,将在“except:”语句之后handle无效的情况下调用close,报错handle未定义。
  187. ```python
  188. try:
  189. handle = open(r"/tmp/sample_data.txt") # May raise IOError
  190. data = handle.read() # May raise UnicodeDecodeError
  191. except:
  192. handle.close()
  193. ```
  194. 【正确代码示例】
  195. ```python
  196. try:
  197. handle = open(r"/tmp/sample_data.txt") # May raise IOError
  198. try:
  199. data = handle.read() # May raise UnicodeDecodeError
  200. except UnicodeDecodeError as decode_error:
  201. print(decode_error)
  202. finally:
  203. handle.close()
  204. except(FileNotFoundError, IOError) as file_open_except:
  205. print(file_open_except)
  206. ```
  207. <font size=3>**规则 2.3.4 不在except分支里面的raise都必须带异常。**</font>
  208. raise关键字单独使用只能出现在try-except语句中,重新抛出except抓住的异常。
  209. 【错误代码示例】
  210. ```python
  211. a = 1
  212. if a==1:
  213. raise
  214. ```
  215. 【正确代码示例1】raise一个Exception或自定义的Exception
  216. ```python
  217. a = 1
  218. if a==1:
  219. raise Exception
  220. ```
  221. 【正确代码示例2】在try-except语句中使用
  222. ```python
  223. try:
  224. f = open('myfile.txt')
  225. s = f.readline()
  226. i = int(s.strip())
  227. except IOError as e:
  228. print("I/O error({0}): {1}".format(e.errno, e.strerror))
  229. except ValueError:
  230. print("Could not convert data to an integer.")
  231. except Exception:
  232. print("Unexpected error:", sys.exc_info()[0])
  233. raise
  234. ```
  235. #### 2.4 序列化和反序列化
  236. <font size=3>**规则 2.4.1 pickle存在安全性问题,禁止使用pickle.load、cPickle.load和shelve模块加载不可信数据。**
  237. <font size=3>**规则 2.4.2 使用安全随机数。**</font>
  238. Python产生随机数的功能在random模块中实现,实现了各种分布的伪随机数生成器。产生的随机数可
  239. 以是均匀分布,高斯分布,对数正态分布,负指数分布以及alpha,beta分布,但是这些随机数都是伪随机数,不
  240. 能应用于安全加密目的的应用中。
  241. 请使用/dev/random生成安全随机数,或者使用在python 3.6版本官方引入的secrets模块生成安全随机数。
  242. 【错误代码示例】
  243. ```python
  244. import random
  245. # 伪随机数
  246. func = random.SystemRandom()
  247. print(func.random())
  248. print(func.randint(0, 10))
  249. ```
  250. 【正确代码示例】
  251. ```python
  252. import platform
  253. # 长度请参见密码算法规范,不同场景要求长度不一样
  254. randLength = 16
  255. if platform.system() == 'Linux':
  256. with open("/dev/random", 'rb') as file:
  257. sr = file.read(randLength)
  258. print(sr)
  259. ```
  260. <font size=3>**规则 2.4.3 assert语句通常只在测试代码中使用,禁止在Release版本中包含assert功能。**</font>
  261. assert只应在研发过程中内部测试时使用,出现了AssertionError异常说明存在软件设计或者编码上的错误,应
  262. 当修改软件予以解决。在对外发布的生产版本中禁止包含assert功能。
  263. ------------------------