警惕!Python中少为人知的10个安全陷阱
作者:DennisBrinkrolf
译者:豌豆花下猫Python猫
原题:10UnknownSecurityPitfallsforPython
英文:https:blog。sonarsource。com10unknownsecuritypitfallsforpython
声明:本翻译是出于交流学习的目的,基于CCBYNCSA4。0授权协议。为便于阅读,内容略有改动。
Python开发者们在使用标准库和通用框架时,都以为自己的程序具有可靠的安全性。然而,在Python中,就像在任何其它编程语言中一样,有一些特性可能会被开发者们误解或误用。通常而言,只有极少的微妙之处或细节会使开发者们疏忽大意,从而在代码中引入严重的安全漏洞。
在这篇博文中,我们将分享在实际Python项目中遇到的10个安全陷阱。我们选择了一些在技术圈中不太为人所知的陷阱。通过介绍每个问题及其造成的影响,我们希望提高人们对这些问题的感知,并提高大家的安全意识。如果你正在使用这些特性,请一定要排查你的Python代码!1。被优化掉的断言
Python支持以优化的方式执行代码。这使代码运行得更快,内存用得更少。当程序被大规模使用,或者可用的资源很少时,这种方法尤其有效。一些预打包的Python程序提供了优化的字节码。
然而,当代码被优化时,所有的assert语句都会被忽略。开发者有时会使用它们来判断代码中的某些条件。例如,如果使用断言来作身份验证检查,则可能导致安全绕过。defsuperuseraction(request,user):assertuser。issuperuserexecuteactionassuperuser
在这个例子中,第2行中的assert语句将被忽略,导致非超级用户也可以运行到下一行代码。不推荐使用assert语句进行安全相关的检查,但我们确实在实际的项目中看到过它们。2。MakeDirs权限
os。makdirs函数可以在操作系统中创建一个或多个文件夹。它的第二个参数mode用于指定创建的文件夹的默认权限。在下面代码的第2行中,文件夹ABC是用rwx(0o700)权限创建的。这意味着只有当前用户(所有者)拥有这些文件夹的读、写和执行权限。definitdirectories(request):os。makedirs(ABC,mode0o700)returnHttpResponse(Done!)
在Python3。6版本中,创建出的文件夹A、B和C的权限都是700。但是,在Python3。6版本中,只有最后一个文件夹C的权限为700,其它文件夹A和B的权限为默认的755。
因此,在Python3。6中,os。makdirs函数等价于Linux的这条命令:mkdirm700pABC。有些开发者没有意识到版本之间的差异,这已经在Django中造成了一个权限越级漏洞(cve202224583),无独有偶,这在WordPress中也造成了一个加固绕过问题。3。绝对路径拼接
os。path。join(path,paths)函数用于将多个文件路径连接成一个组合的路径。第一个参数通常包含了基础路径,而之后的每个参数都被当做组件拼接到基础路径后。
然而,这个函数有一个少有人知的特性。如果拼接的某个路径以开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将被视为绝对路径。下面的示例揭示了开发者可能遇到的这个陷阱。defreadfile(request):filenamerequest。POST〔filename〕filepathos。path。join(var,lib,filename)iffilepath。find(。)!1:returnHttpResponse(Failed!)withopen(filepath)asf:returnHttpResponse(f。read(),contenttypetextplain)
在第3行中,我们使用os。path。join函数将用户输入的文件名构造出目标路径。在第4行中,检查生成的路径是否包含。,防止出现路径遍历漏洞。
但是,如果攻击者传入的文件名参数为abc。txt,那么第3行得到的变量filepath会是一个绝对路径(abc。txt)。即os。path。join会忽略掉varlib部分,攻击者可以不使用。字符就读取到任何文件。尽管os。path。join的文档中描述了这种行为,但这还是导致了许多漏洞(CuckooSandboxEvasion,CVE202035736)。4。任意的临时文件
tempfile。NamedTemporaryFile函数用于创建具有特定名称的临时文件。但是,prefix(前缀)和suffix(后缀)参数很容易受到路径遍历攻击(Issue35278)。如果攻击者控制了这些参数之一,他就可以在文件系统中的任意位置创建出一个临时文件。下面的示例揭示了开发者可能遇到的一个陷阱。deftouchtmpfile(request):idrequest。GET〔id〕tmpfiletempfile。NamedTemporaryFile(prefixid)returnHttpResponse(ftmpfile:{tmpfile}created!,contenttypetextplain)
在第3行中,用户输入的id被当作临时文件的前缀。如果攻击者传入的id参数是。。varwwwtest,则会创建出这样的临时文件:varwwwtestzdllj17。粗看起来,这可能是无害的,但它会为攻击者创造出挖掘更复杂的漏洞的基础。5。扩展的ZipSlip
在Web应用中,通常需要解压上传后的压缩文件。在Python中,很多人都知道TarFile。extractall与TarFile。extract函数容易受到ZipSlip攻击。攻击者通过篡改压缩包中的文件名,使其包含路径遍历(。。)字符,从而发起攻击。
这就是为什么压缩文件应该始终被视为不受信来源的原因。zipfile。extractall与zipfile。extract函数可以对zip内容进行清洗,从而防止这类路径遍历漏洞。
但是,这并不意味着在ZipFile库中不会出现路径遍历漏洞。下面是一段解压缩文件的代码。defextracthtml(request):filenamerequest。FILES〔filename〕zfzipfile。ZipFile(filename。temporaryfilepath(),r)forentryinzf。namelist():ifentry。endswith(。html):filecontentzf。read(entry)withopen(entry,wb)asfp:fp。write(filecontent)zf。close()returnHttpResponse(HTMLfilesextracted!)
第3行代码根据用户上传文件的临时路径,创建出一个ZipFile处理器。第48行代码将所有以。html结尾的压缩项提取出来。第4行中的zf。namelist函数会取到zip内压缩项的名称。注意,只有zipfile。extract与zipfile。extractall函数会对压缩项进行清洗,其它任何函数都不会。
在这种情况下,攻击者可以创建一个文件名,例如。。。。。。varwwwhtml,内容随意填。该恶意文件的内容会在第6行被读取,并在第78行写入被攻击者控制的路径。因此,攻击者可以在整个服务器上创建任意的HTML文件。
如上所述,压缩包中的文件应该被看作是不受信任的。如果你不使用zipfile。extractall或者zipfile。extract,你就必须对zip内文件的名称进行消毒,例如使用os。path。basename。否则,它可能导致严重的安全漏洞,就像在NLTKDownloader(CVE201914751)中发现的那样。6。不完整的正则表达式匹配
正则表达式(regex)是大多数Web程序不可或缺的一部分。我们经常能看到它被自定义的Web应用防火墙(WAF,WebApplicationFirewalls)用来作输入验证,例如检测恶意字符串。在Python中,re。match和re。search之间有着细微的区别,我们将在下面的代码片段中演示。defissqlinjection(request):patternre。compile(r。(union)(select)。)nametotestrequest。GET〔name〕ifre。search(pattern,nametotest):returnTruereturnFalse
在第2行中,我们定义了一个匹配union或者select的模式,以检测可能的SQL注入。这是一个糟糕的写法,因为你可以轻易地绕过这些黑名单,但我们已经在线上的程序中见过它。在第4行中,函数re。match使用前面定义好的模式,检查第3行中的用户输入内容是否包含这些恶意的值。
然而,与re。search函数不同的是,re。match函数不匹配新行。例如,如果攻击者提交了值aaaaaaunionselect,这个输入就匹配不上正则表达式。因此,检查可以被绕过,失去保护作用。
总而言之,我们不建议使用正则表达式黑名单进行任何安全检查。7。Unicode清洗器绕过
Unicode支持用多种形式来表示字符,并将这些字符映射到码点。在Unicode标准中,不同的Unicode字符有四种归一化方案。程序可以使用这些归一化方法,以独立于人类语言的标准方式来存储数据,例如用户名。
然而,攻击者可以利用这些归一化,这已经导致了Python的urllib出现漏洞(CVE20199636)。下面的代码片段演示了一个基于NFKC归一化的跨站点脚本漏洞(XSS,CrossSiteScripting)。importunicodedatafromdjango。shortcutsimportrenderfromdjango。utils。htmlimportescapedefrenderinput(request):userinputescape(request。GET〔p〕)normalizeduserinputunicodedata。normalize(NFKC,userinput)context{myinput:normalizeduserinput}returnrender(request,test。html,context)
在第6行中,用户输入的内容被Django的escape函数处理了,以防止XSS漏洞。在第7行中,经过清洗的输入被NFKC算法归一化,以便在第89行中通过test。html模板正确地渲染。
templatestest。html!DOCTYPEhtmlhtmllangenbody{{myinputsafe}}bodyhtml
在模板test。html中,第4行的变量myinput被标记为安全的,因为开发人员预期有特殊字符,并且认为该变量已经被escape函数清洗了。通过标记关键字safe,Django不会再次对变量进行清洗。
但是,由于第7行(view。py)的归一化,字符EFB9A4会被转换为,EFB9A5被转换为。这导致攻击者可以注入任意的HTML标记,进而触发XSS漏洞。为了防止这个漏洞,就应该在把用户输入做完归一化之后,再进行清洗。8。Unicode编码碰撞
前文说过,Unicode字符会被映射成码点。然而,有许多不同的人类语言,Unicode试图将它们统一起来。这就意味着不同的字符很有可能拥有相同的layout。例如,小写的土耳其语(没有点)的字符是英语中大写的I。在拉丁字母中,字符i也是用大写的I表示。在Unicode标准中,这两个不同的字符都以大写形式映射到同一个码点。
这种行为是可以被利用的,实际上已经在Django中导致了一个严重的漏洞(CVE201919844)。下面的代码是一个重置密码的示例。fromdjango。core。mailimportsendmailfromdjango。httpimportHttpResponsefromvuln。modelsimportUserdefresetpw(request):emailrequest。GET〔email〕resultUser。objects。filter(emailexactemail。upper())。first()ifnotresult:returnHttpResponse(Usernotfound!)sendmail(ResetPassword,Yournewpw:123456。,fromexample。com,〔email〕,failsilentlyFalse)returnHttpResponse(Passwordresetemailsend!)
第6行代码获取了用户输入的email,第79行代码检查这个email值,查找是否存在具有该email的用户。如果用户存在,则第10行代码依据第6行中输入的email地址,给用户发送邮件。需要指出的是,第79行中对邮件地址的检查是不区分大小写的,使用了upper函数。
至于攻击,我们假设数据库中存在一个邮箱地址为foomix。com的用户。那么,攻击者可以简单地传入foomx。com作为第6行中的email,其中i被替换为土耳其语。第7行代码将邮箱转换成大写,结果是FOOMIX。COM。这意味着找到了一个用户,因此会发送一封重置密码的邮件。
然而,邮件被发送到第6行未转换的邮件地址,也就是包含了土耳其语的。换句话说,其他用户的密码被发送到了攻击者控制的邮件地址。为了防止这个漏洞,可以将第10行替换成使用数据库中的用户邮箱。即使发生编码冲突,攻击者在这种情况下也得不到任何好处。9。IP地址归一化
在Python3。8中,IP地址会被ipaddress库归一化,因此前缀的零会被删除。这种行为乍一看可能是无害的,但它已经在Django中导致了一个高严重性的漏洞(CVE202133571)。攻击者可以利用归一化绕过校验程序,发起服务端请求伪造攻击(SSRF,ServerSideRequestForgery)。
下面的代码展示了如何绕过这样的校验器。importrequestsimportipaddressdefsendrequest(request):iprequest。GET〔ip〕try:ifipin〔127。0。0。1,0。0。0。0〕:returnHttpResponse(Notallowed!)ipstr(ipaddress。IPv4Address(ip))exceptipaddress。AddressValueError:returnHttpResponse(Erroratvalidation!)requests。get(https:ip)returnHttpResponse(Requestsend!)
第5行代码获取用户传入的一个IP地址,第7行代码使用一个黑名单来检查该IP是否为本地地址,以防止可能的SSRF漏洞。这份黑名单并不完整,仅作为示例。
第9行代码检查该IP是否为IPv4地址,同时将IP归一化。在完成验证后,第12行代码会对该IP发起实际的请求。
但是,攻击者可以传入127。0。001这样的IP地址,在第7行的黑名单列表中找不到。然后,第9行代码使用ipaddress。IPv4Address将IP归一化为127。0。0。1。因此,攻击者就能够绕过SSRF校验器,并向本地网络地址发送请求。10。URL查询参数解析
在Python3。7中,urllib。parse。parseqsl函数允许使用;和字符作为URL的查询变量的分隔符。有趣的是;字符不能被其它语言识别为分隔符。
在下面的例子中,我们将展示为什么这种行为会导致漏洞。假设我们正在运行一个基础设施,其中前端是一个PHP程序,后端则是一个Python程序。
攻击者向PHP前端发送以下的GET请求:GEThttps:victim。com?a1;b2
PHP前端只识别出一个查询参数a,其内容为1;b2。PHP不把;字符作为查询参数的分隔符。现在,前端会将攻击者的请求直接转发给内部的Python程序:GEThttps:internal。backend?a1;b2
如果使用了urllib。parse。parseqsl,Python程序会处理成两个查询参数,即a1和b2。这种查询参数解析的差异可能会导致致命的安全漏洞,比如Django中的Web缓存投毒漏洞(CVE202123336)。总结
在这篇博文中,我们介绍了10个Python安全陷阱,我们认为开发者不太了解它们。每个细微的陷阱都很容易被忽视,并在过去导致了线上程序的安全漏洞。
正如前文所述,安全陷阱可能出现在各种操作中,从处理文件、目录、压缩文件、URL、IP到简单的字符串。一种常见的情况是库函数的使用,这些函数可能有意想不到的行为。这提醒我们一定要升级到最新版本,并仔细阅读文档。在SonarSource中,我们正在研究这些缺陷,以便将来不断改进我们的代码分析器。
SpringBoot如何使用Caffeine缓存引入caffeine坐标dependencygroupIdcom。github。benmanes。caffeinegroupIdcaffeineartifactIdversio……
交通运输部约谈提醒滴滴出行货拉拉等8家平台公司交通运输部微信公众号交通运输部。图源:视觉中国2022年1月20日上午,交通运输新业态协同监管部际联席会议办公室对满帮、货拉拉、滴滴货运、快狗打车等4家互联网道路货……
收藏生活的美丽作文700字转眼间,从初一走进校园的我们,已即将进入初三了,我们在一起经历过多少风风雨雨,窗户外的那一棵小树,如今也长高了。点点滴滴我们收藏者生活中的美丽。雨天在校园中漫步,聆听雨滴……
SpringBoot系列springdevtools模块springbootdevtools是SpringBoot给开发人员提供的一个工具包,它可以提高开发人员的工作效率。我们可以通过如下方式引入该工具包:Mavendepen……
手机跑分有什么用手机跑分通常包括UE测试(多任务与虚拟机)、CPU整体性能测试、RAM内存测试、2D3D图形性能测试以及数据存储IO的性能检测。1、显卡跑分是用3dmark或者相关软件来……
鞭牛晚报吉利回应拟收购魅族手机网易云音乐举报冒名洗歌等侵权行编者按:鞭牛士将以晚报形式盘点今日内发生的重要事件,内容涵盖国际、国内科技互联网,为科技行业从业者、用户传递行业信息。国内动态1、传吉利手机业务拟收购魅族,吉利:不……
阿凡达观后感范文这个故事讲述了一个名叫杰克的阿凡达来到潘多拉星球做任务,但后来,他被这里的土著人拼死都要守护家园的精神深深地感动了,最后帮助潘多拉人击退地球人的故事。杰克第一次来到潘多拉的时候……
有没有15年以上没换手机号的朋友,晒晒你的手机号后5位?我的移动手机号,用了大概十三年左右。今年携号转网,转到了联通。换网不换号,继续用下去。后五位54014,谐音我死了又死。这个号码是我上学那会跟报停买的,那会手机号还没有实……
那一次我读懂了父爱作文800字我曾读过许许多多有关母爱的书,文中说母爱是这个世上最最神圣的,而写父爱的文章却很少很少,这也难怪,在生活中,父亲扮演的大多是严肃、冷漠的角色,我爸也是这样。我爸爸在我眼里……
今日头条天天给大家发钱,那今日头条的钱从哪里来的啊?今日头条是国内为数不多的百亿级媒体资讯平台,有着强大的资金,数据,流量支持,为了更好的增强用户体验以及回馈新老用户,特推出了今日头条极速版,今日头条极速版包罗万象,让人们在娱乐……
春节游记500字作文今年春节期间,我们一家来到了历史文化名称之一的绍兴。一下火车,就感到了一种浓浓的春节气息:火车站里挂着红灯笼,贴着对联,玻璃门上贴着福字,一切都让人有一种温馨的感觉。出了……
共享单车下的文明六年级作文一辆辆崭新的小黄车,停放在城市的各个角落,成了石家庄街头的一道亮丽的风景。在我的软磨硬泡下,妈妈也下载了APP,从此小黄车成了我的出行最爱。周六,阳光灿烂,我和妈妈商量好……
让自己强大英语作文Successcan39;tbeexpectedtobeopportunistic,theonlywaytosucceedistomakeyourselfstrong。Just……
珍爱生命,遵守规则生命,这是再熟悉不过的俩字啦。生命是唯一的,是宝贵的,世界因为有了生命而变得精彩。您的生命,您珍惜吗?要想生命得到保障,请您遵守交通规则。在这经济繁荣的社会中,更多的车辆会在你……
小学生写亲人的作文我的妹妹品学网:《我的妹妹》写了小作者的妹妹的性格特点,让人对其有了一个深刻的印象。下面我们来看看这篇《我的妹妹》。我的妹妹很可爱!她有两颗黑葡萄般的大眼睛,时不时地转着,是个小……
帮父母洗脚的作文倒完洗脚水,我又回到卧室,让妈妈趴在床上,为她敲打、按摩双腿,妈妈直说舒服,我就对妈妈说:今后我天天为您洗脚、按摩、敲打。这时,妈妈坐了起来,眼里闪烁着晶莹的泪花。以下内容是小……
Docker的基本使用方法Docker是常用的容器产品,通过使用Docker,可以实现基本的微服务部署架构。下面介绍一下Docker的基本使用方法查看镜像状态dockerimagels查看容……
微信里有几个网购优惠券的群,也使用过里面的优惠券。这段时间发这个问题可以通过三个方面来回答:第一:这是很正常的,没有什么奇怪的,不管你在哪里买过什么东西,或者搜索过什么东西都有痕迹的,他们都会留有你的信息的,我们平常在手机上经常在……
机械硬盘不分区速度更快固态硬盘分区不影响寿命我给电脑里的小姐姐买了豪华别墅了,而且是4T大平层,再也不怕住不下啦!等等,你买的新硬盘分区了吗?如果纠结于想给硬盘分区,但又担心分区会影响硬盘的速度和寿命,不妨随我走上DIY……
勇于冒险哲理小故事对于那些害怕危险的人,危险无处不在。有一天,龙虾与寄居蟹在深海中相遇,寄居蟹看见龙虾正把自己的硬壳脱掉,只露出娇嫩的身躯。寄居蟹非常紧张地说:龙虾,你怎可以把唯一保护自己……
描写兵马俑作文范文5篇描写兵马俑作文范文1今天,爸爸给我带回来了一个精美的礼品盒。盒子的正面是用隶书写的四个字中国秦俑。我感到很好奇,什么是秦俑呢?我带着十分好奇的心急忙打开了那个礼品盒。发现……
珍爱生命燕子去了,有再来的时候;杨柳枯了,有再青时候;桃花谢了,有再开的时候。但是,聪明的你,告诉我们的生命为什么一去不复返呢?因为生命只有一次。你死了,就不能复生。世上什么丹药都有,……
多家锂电产业链厂商加码布局电池回收业务本报记者金一丹宁德时代宣布,邦普一体化新能源产业项目于12月4日正式开工,对退役电池回收再利用以实现降本。近期,华友钴业、中伟股份等多家锂电产业链厂商亦表示布局动力电池回……
大公司早报张勇称要变革阿里的生产关系小红书回应推送未成年人身(整理胡毓靖)今天是12月6日星期一,过去一个周末的大公司新闻包括:阿里宣布升级多元化治理体系,张勇要变革阿里巴巴的生产关系;小红书被曝推送未成年人身体隐私,将启动新一轮……