標簽:report 常用 表達式 lis 數據格式 dimen ioerror return byte
文章目錄
9.1 异常行为
9.1.1 禁止抑制或者忽略已检查异常
9.1.2 禁止在异常中泄露敏感信息
9.1.3 方法发生异常时要恢复到之前的对象状态
9.2 运行环境
9.2.1 生产代码不能包含任何调试入口点
9.2.2 使用标准的API替代操作系统的系统命令
9.2.3 禁止从第3方源下载并使用软件包
9.3 其他
9.3.1 禁止在日志中保存口令、密钥等敏感信息
9.3.2 禁止将敏感信息硬编码在程序中
9.3.3 使用安全随机数
9.3.4 代码发布前务必书写开发者信息及包含开发者信息的注释内容
9.3.5 保存外来不可信数据前要先转义
9.1 异常行为
9.1.1 禁止抑制或者忽略已检查异常
说明: 编码人员常常会通过一个空的或者无意义的except块来抑制捕获的已检查异常。每一个except块都应该确保程序只会在继续有效的情况下才会继续运行下去。因此,except块必须要么从异常情况中恢复,要么重新抛出适合当前except块上下文的另一个异常以允许最邻近的外层try-except语句块来进行恢复工作。异常会打断应用原本预期的控制流程。例如,try块中位于异常发生点之后的任何表達式和语句都不会被执行。因此,异常必须被妥当处理。许多抑制异常的理由都是不合理的。例如,当对客户端从潜在问题恢复过来不抱期望时,一种好的做法是让异常被广播出来,而不是去捕获和抑制这个异常。
# 【錯誤示例】: 下面代码假设获取客户端输入的Email地址,在使用之前对Email进行數據格式的合法性校验。
try:
# to_do
pass
except Exception as ex:
import traceback
traceback.print_exc()
# 错误示例中, except 块只是简单地将异常堆栈轨迹打印出来。
# 虽然打印异常的堆栈轨迹对于定位问题是有帮助的,但是最终的程序运行逻辑等同于抑制异常。
# 注意,即使这个错误示例在发生异常时会打印一个堆栈轨迹,但是程序会继续运行,如同异常从未被抛出过。
# 换句话说,除了try块中位于异常发生点之后的表達式和语句不会被执行之外,发生的异常不会影响程序的其他行为。
# 【正确示例1】:
validFlag = False
while not validFlag:
try:
# If requested file does not exist, throws FileNotFoundException
# If requested file exists, sets validFlag to true
validFlag = True
pass
except FileNotFoundException:
import traceback
traceback.print_exc()
# 【正确示例2】:
# 继承Exception Reporter并过滤敏感异常。有时候异常必须对用户隐藏。
# 在这种情况下,一种可行的方式是继承Exception类,并且在重写默认的str方法的基础上,增加一个filter()方法。
class MyExceptionReporter(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return filter(self.msg)
def filter(self):
# Sanitize sensitive data or replace sensitive exceptions with non-sensitive exceptions (whitelist)
# Return non-sensitive exception
pass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
例外情況:
在資源釋放失敗不會影響程序後續行爲的情況下,釋放資源時發生的異常可以被抑制。釋放資源的例子包括關閉文件、網絡套接字、線程等等。這些資源通常是在except或者fianlly塊中被釋放,並且在後續的程序運行中都不會再被使用。因此,除非資源被耗盡,否則不會有其他途徑使得這些異常會影響程序後續的行爲。在充分處理了資源耗盡問題的情況下,只需對異常進行淨化和記錄日志(以備日後改進)就足夠了;在這種情況下沒必要做其他額外的錯誤處理。
如果在特定的抽象層次上不可能從異常情況中恢複過來,則在那個層級的代碼就不用處理這個異常,而是應該抛出一個合適的異常,讓更高層次的代碼去捕獲處理,並嘗試恢複。對于這種情況,最通常的實現方法是省略掉except語句塊,允許異常被廣播出去。
9.1.2 禁止在异常中泄露敏感信息
说明: 敏感数据的范围应该基于应用场景以及产品威胁分析的结果来确定。典型的敏感数据包括口令、银行账号、个人信息、通讯记录、密钥等。如果在传递异常的时候未对其中的敏感信息进行过滤常常会导致信息泄露,而这可能帮助攻击者尝试发起进一步的攻击。攻击者可以通过构造恶意的输入参数来发掘应用的内部结构和机制。不管是异常中的文本消息,还是异常本身的类型都可能泄露敏感信息。例如,对于IOError异常,其中的异常消息会透露文件系统的结构信息,而通过异常本身的类型,可以得知所请求的文件不存在。因此,当异常会被传递到信任边界以外时,必须同时对敏感的异常消息和敏感的异常类型进行过滤。
# 【錯誤示例】:
import sys
if len(sys.argv) != 2:
print("Invalid file")
else:
try:
open(sys.argv[1], "r")
except MyError as e:
# Log the exception
pass
# 如果传入一个文件名后,程序返回一个异常,则暗示该文件不存在,而如果不抛出异常则说明该文件是存在的。
# 使得系统面临暴力攻击的风险,攻击者可以多次传入不同的文件名进行暴力碰撞来发现有效文件。
# 【正确示例】:
import sys
if len(sys.argv) != 2:
print ("Invalid file")
else:
# 先把路径进行标准化
try:
truepath = os.path.realpath(sys.argv[1])
except Exception as ex:
print ("Invalid file")
# 校验路径是否以/home/python/开头
if truepath.startswith(‘/home/python/‘):
try:
open(truepath)
except IOError:
print("Invalid file")
else:
print("Invalid file")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
在這個正確示例中,規定用戶只能打開/home/python/目錄下的文件,用戶不可能發現這個目錄以外的任何信息。
在這個方案中,如果無法打開文件,或者文件不在合法的目錄下,則會産生一條簡潔的錯誤消息。
9.1.3 方法发生异常时要恢复到之前的对象状态
說明:當發生異常的時候,對象一般需要(關鍵的安全對象則必須)維持其狀態的一致性。常用的可用來維持對象狀態一致性的手段包括:輸入校驗(如校驗方法的調用參數)調整邏輯順序,使可能發生異常的代碼在對象被修改之前執行當業務操作失敗時,進行回滾對一個臨時的副本對象進行所需的操作,直到成功完成這些操作後,才把更新提交到原始的對象避免去修改對象狀態
# 【錯誤示例】:
PADDING = 2
MAX_DIMENSION = 10
class Dimensions(object):
def __init__(self, length, width, height):
self.length = length
self.width = width
self.height = height
def getVolumePackage(self, weight):
self.length += PADDING
self.width += PADDING
self.height += PADDING
try:
self.validate(weight)
volume = self.length * self.width * self.height
self.length -= PADDING
self.width -= PADDING
self.height -= PADDING
return volume
except Exception as e:
return -1
def validate(self, weight):
# do some validation and may throw a exception
if weight > 20:
raise Exception
pass
if __name__ == ‘__main__‘:
d = Dimensions(10, 10, 10)
print(d.getVolumePackage(21)) # Prints -1 (error)
print(d.getVolumePackage(19)) # Prints 2744 instead of 1728
# 在这个错误示例中,未有异常发生时,代码逻辑会恢复对象的原始状态。
# 但是如果出现异常事件,则回滚代码不会被执行,从而导致后续的 getVolumePackage() 调用不会返回正确的结果。
# 【正确示例1】回滚:
...
except Exception as e:
self.length -= PADDING
self.width -= PADDING
self.height -= PADDING
return -1
# 这个正确示例在getVolumePackage() 方法的except 块中加入了发生异常时恢复对象状态的代码。
# 【正确示例2】finally分支:
def getVolumePackage(self, weight):
self.length += PADDING
self.width += PADDING
self.height += PADDING
try:
self.validate(weight)
volume = self.length * self.width * self.height
return volume
except Exception as e:
return -1
finally:
self.length -= PADDING
self.width -= PADDING
self.height -= PADDING
# 这个正确示例使用一个finally子句来执行回滚操作,以保证不管是否发生异常,都会进行回滚。
# 【正确示例3】输入校验:
def getVolumePackage(self, weight):
try:
self.validate(weight)
except Exception as e:
return -1
self.length += PADDING
self.width += PADDING
self.height += PADDING
volume = self.length * self.width * self.height
self.length -= PADDING
self.width -= PADDING
self.height -= PADDING
return volume
# 这个正确示例在修改对象状态之前执行输入校验。注意,try代码块中只包含可能会抛出异常的代码,而其他代码都被移到 try 块之外。
# 【正确示例4】未修改的对象:
def getVolumePackage(self, weight):
try:
self.validate(weight)
except Exception as e:
return -1
volume = (self.length + PADDING) * (self.width + PADDING) * (self.height + PADDING)
return volume
# 这个正确示例避免了需要修改对象,使得对象状态不可能不一致,也因此没有必要进行回滚操作。
# 相比之前的解决方案,更推荐使用这种方式。但是对于一些复杂的代码,这种方式可能无法实行。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
9.2 运行环境
9.2.1 生产代码不能包含任何调试入口点
说明: 由于调试或者测试目的,开发者经常在代码中添加特定的调测代码,这些代码并没有打算与应用一起交付或者部署。当这类的调测代码不小心被留在了应用中,这个应用对某些特殊交互就是开放的。这些后门入口点可以导致安全风险。
【錯誤示例】:
def wrong(flag):
if not flag:
import pdb
pdb.set_trace()
print(‘.....‘)
...
# end def
1
2
3
4
5
6
7
【建議】:
代碼在交付前務必刪除所有調試相關的代碼!
9.2.2 使用标准的API替代操作系统的系统命令
說明:如果可以使用標准的API替代運行系統命令來完成任務,則應該使用標准的API。
當前與Python相關的漏洞中命令注入占比非常大,所以在此呼籲不要直接使用系統命令,不建議使用os.system、subprocess等模塊去運行系統命令,如遍曆目錄/文件/創建文件夾等操作。
【建議】: 在python的标准库中有多种方法实现与执行系统命令同样的功能,如os.listdir、glob.glob、glob.glob1 等模块都可以实现系统命令“ls” 或“dir”的功能,所以此处倡议不要直接调用系统命令:
dir
cmd.exe
powershell.exe
ls
awk
cat
等windows、linux及mac的系統命令
9.2.3 禁止从第3方源下载并使用软件包
说明: Python之所以如此流行的原因之一是其生态做的好, 其import非常灵活,方便使用的同时也存在着安全漏洞。目前Python依赖项中有5000多个已知的安全漏洞,这些都可能导致您自己的代码中出现严重的安全漏洞。别有用心的hacker已经在篡改了大量的软件包并发布的互联网上,一旦使用就会导致不可挽回的损失。
【建議】:
从官网下载并使用流行的软件包,使用前请确保全局的 site-packages 尽可能的干净(比如工程环境只用单独的虚拟环境),同时检查包签名。
9.3 其他
9.3.1 禁止在日志中保存口令、密钥等敏感信息
L說明:在日志中不能輸出口令、密鑰和其他敏感信息,口令包括明文口令和密文口令。對于敏感信息建議采取以下方法:不在日志中打印敏感信息。
若因爲特殊原因必須要打印日志,則用固定長度的星號(*)代替輸出的敏感信息。
9.3.2 禁止将敏感信息硬编码在程序中
说明: 如果将敏感信息(包括口令和加密密钥)硬编码在程序中,可能会将敏感信息暴露给攻击者。无需反编译pyc文件,任何能够访问到pyc文件的人都可以获取这些敏感信息。因此,不能将敏感信息硬编码在程序中。
【錯誤示例】:
class DefPassword:
defPassword = "123456"
1
2
惡意用戶可以直接打開pyc文件發現其中硬編碼的默認密碼是:123456。
【建議】: 建议加密后使用。
9.3.3 使用安全随机数
说明: Python产生随机数的功能在random模块中实现,实现了各种分布的伪随机数生成器。产生的随机数可以是均匀分布,高斯分布,对数正态分布,负指数分布以及alpha,beta分布,但是这些随机数都是伪随机数,不能应用于安全加密目的的应用中。如果你需要一个真正的密码安全随机数,请使用/dev/random生成安全随机数;另外在python 3.6版本官方引入了一个secrets模块用于生成安全随机数。
# 【错误示例1】:
import random
sr = random.randint(0, 100)
# 【错误示例2】:
import random
foo = random.SystemRandom() # 伪随机数
print(foo.random())
print(foo.randint(0, 10))
# random 模块还提供 SystemRandom 类,它使用系统函数 os.urandom() 从操作系统提供的源生成随机数。
# 注意: os.urandom 在linux系统环境中生成的随机数不安全,不符合我司标准。
# 在Python 3.6及更高版本中添加了secrets模块,可以使用secrets模块来实现:生成安全随机数、密码及一次性密码、随机token及会话密钥等功能。
# 【正确示例1】:linux环境下推荐使用此方法
import platform
randLength = 16 # 长度请参见密码算法规范,不同场景要求长度不一样
if platform.system() == ‘Linux‘:
with open("/dev/random", ‘rb‘) as file:
sr = file.read(randLength)
print(sr)
# 【正确示例2】:windows环境下推荐
import platform
import os
randLength = 16 # 长度请参见密码算法规范,不同场景要求长度不一样
if platform.system() == ‘Windows‘:
_randLst = list(os.urandom(randLength))
print(_randLst)
_randBytes = os.urandom(randLength)
print(_randBytes)
# 【正确示例3】:
# 适用于python3.6之后的版本
import secrets
# 生成随机整数
number = secrets.randbelow(10)
print("Secure random number is ", number)
# Secure random number is 0
secretsGenerator = secrets.SystemRandom()
randomNumber = secretsGenerator.randint(0, 50)
print("Secure random number is ", randomNumber)
# Secure random number is 26
# 指定范围并设置步长
randomNumber = secretsGenerator.randrange(5, 50, 5)
print("Secure random number within range is ", randomNumber)
# Secure random number within range is 15
# 从指定的数据集中选择
number_list = [6, 12, 18, 24, 30, 36, 42, 48, 54, 60]
secure_choice = secretsGenerator.choice(number_list)
print("Secure random choice using secrets is ", secure_choice)
# Secure random choice using secrets is 30
secure_sample = secretsGenerator.sample(number_list, 3)
print("Secure random sample using secrets is ", secure_sample)
# Secure random sample using secrets is [54, 6, 42]
# 随机安全浮点数
secure_float = secretsGenerator.uniform(2.5, 25.5)
print("Secure random float number using secrets is ", secure_float)
# Secure random float number using secrets is 9.445927455984885
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
9.3.4 代码发布前务必书写开发者信息及包含开发者信息的注释内容
说明:RPA层面为了防止人员流动引起的后期维护困难 , 所有python脚本代码文件开头书写开发者信息的注释内容。
【建議】:
# -*- coding : utf-8
# -*- Author : x00000000
# -*- Date : 20200505
# -*- version : Python 3.7.4
1
2
3
4
代碼發布前,書寫所有與開發者信息相關的注釋信息。
9.3.5 保存外来不可信数据前要先转义
說明:此規則來源于CSV命令注入漏洞案例。
CSV命令注入漏洞:CSV的命令注入漏洞是当用户打开存在特殊字符构造成表達式的CSV格式或Excel文档时,宿主程序(微软的Excel软件)会解析并执行该表達式,从而运行攻击者的指令,达到攻击本地计算机系统的目的。这种攻击多发生于web系统的导出日志功能上,攻击形式多样,下面是windows系统上启动了包含特殊字符串的csv文件:=cmd|’/c calc’Hi,2012lib
查看日志文檔的的管理員用戶(因事先了解這僅是自己的日志文件,一般會放松警惕而忽略系統的安全警示)打開該文件時就會發現系統的計算器也被執行了。
所以正確的建議就是謹慎處理用戶的數據,參考前面的規則,引入黑白名單機制,或者對特殊字符進行轉義,此處CSV漏洞屬于特別的場景,可以對=做處理,比如在=號前面插入字符\t,如讓微軟的Excel程序正確解釋即可。提示,包含以上字符串的csv文件被Symantec殺毒軟件報毒爲Trojan.Slakexec!gen4。
【建議】:
在保存文件時,要考慮該文件的閱讀器是否存在缺陷(如此處的Excel注入漏洞)
————————————————
版权声明:本文为CSDN博主「zhao12501」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文鏈接:https://blog.csdn.net/zhao12501/article/details/115473231
標簽:report 常用 表達式 lis 數據格式 dimen ioerror return byte
原文地址:https://www.cnblogs.com/chengjian-physique/p/14995388.html