前端技术
HTML
CSS
Javascript
前端框架和UI库
VUE
ReactJS
AngularJS
JQuery
NodeJS
JSON
Element-UI
Bootstrap
Material UI
服务端和客户端
Java
Python
PHP
Golang
Scala
Kotlin
Groovy
Ruby
Lua
.net
c#
c++
后端WEB和工程框架
SpringBoot
SpringCloud
Struts2
MyBatis
Hibernate
Tornado
Beego
Go-Spring
Go Gin
Go Iris
Dubbo
HessianRPC
Maven
Gradle
数据库
MySQL
Oracle
Mongo
中间件与web容器
Redis
MemCache
Etcd
Cassandra
Kafka
RabbitMQ
RocketMQ
ActiveMQ
Nacos
Consul
Tomcat
Nginx
Netty
大数据技术
Hive
Impala
ClickHouse
DorisDB
Greenplum
PostgreSQL
HBase
Kylin
Hadoop
Apache Pig
ZooKeeper
SeaTunnel
Sqoop
Datax
Flink
Spark
Mahout
数据搜索与日志
ElasticSearch
Apache Lucene
Apache Solr
Kibana
Logstash
数据可视化与OLAP
Apache Atlas
Superset
Saiku
Tesseract
系统与容器
Linux
Shell
Docker
Kubernetes
[JSON冒号变等号的原因]的搜索结果
这里是文章列表。热门标签的颜色随机变换,标签颜色没有特殊含义。
点击某个标签可搜索标签相关的文章。
点击某个标签可搜索标签相关的文章。
JSON
JSON解析冒号变等号:一场误会引发的技术冒险 1. 引言 为什么我们要聊这个? 大家好啊!今天咱们聊聊一个看似简单却可能让人抓狂的话题——JSON解析中冒号变等号的问题。你说这话是不是觉得有点愣?对啊,这种感觉太正常了,特别是你头一回碰见JSON的时候,心里肯定在想:“这是啥东东?”我当年也是懵懵懂懂地开始学编程,结果一不小心就踩进了这个坑里。所以今天想跟大家聊聊我的经历,顺便给大家分享一些解决办法。 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它易于人阅读和编写,同时也易于机器解析和生成。但有时候,这个格式会因为某些小细节而让人头疼不已。哎呀,就拿这个来说吧,你辛辛苦苦敲了一段看着特别标准的JSON数据,结果程序一跑直接给你来个“格式错误”,整得你一头雾水。最后扒拉开一看,嘿,好家伙,罪魁祸首竟然是那个该死的冒号被你手滑打成了等号!哎呀,这种错误简直让人哭笑不得! 不过呢,别担心,今天我会带着大家一起深入探讨这个问题,看看为什么会发生这样的事情,以及如何避免类似的情况再次发生。咱们一起揭开这场“冒号变等号”的谜团吧! --- 2. 什么是JSON?它的基本结构长什么样? 首先,咱们得搞清楚JSON到底是什么。简单来说,JSON是一种用来存储和传输数据的格式。你可以把它想象成一种“万能语言”,不管是搞前端的还是做后端的,大家都能用JSON来互相“说话”、传递信息。 JSON的基本结构其实非常简单,主要由两种元素组成: - 键值对:用冒号:分隔,左边是键(key),右边是值(value)。比如"name": "Alice"。 - 数组:用方括号[]包裹起来的一组值,可以是字符串、数字、布尔值或者嵌套的JSON对象。例如[1, 2, 3]。 示例代码: json { "name": "Alice", "age": 25, "isStudent": false, "courses": ["Math", "Science"] } 这段JSON数据描述了一个学生的信息。你看,整个结构清晰明了,只需要一点点耐心就能读懂。不过嘛,要是这儿的冒号不小心打成了等号=,那整个JSON结构可就直接“翻车”了,啥也跑不出来了!不信的话,咱们试试看。 --- 3. 冒号变等号 一个让人崩溃的小错误 说到冒号变等号,我真的有一肚子的话要说。记得有一次,我在调试一个API接口时,发现返回的数据总是出错。百思不得其解之后,我才意识到问题出在JSON格式上。原来是我手滑,把某个键值对中的冒号写成了等号。 错误示例: json { "name=Alice", "age=25", "isStudent=false", "courses=[Math, Science]" } 看到这里,你是不是也觉得特别别扭?没错,这就是典型的JSON格式错误。正常情况下,JSON中的键值对应该用冒号分隔,而不是等号。等号在这里根本不起作用,会导致整个JSON对象无法被正确解析。 那么问题来了,为什么会有人犯这样的错误呢?我觉得主要有以下几点原因: 1. 疏忽大意 有时候我们写代码太赶时间,注意力不够集中,结果就出现了这种低级错误。 2. 习惯差异 有些人可能来自其他编程语言背景,习惯了用等号作为赋值符号,结果不自觉地把这种习惯带到了JSON中。 3. 工具误导 有些文本编辑器或者IDE可能会自动补全等号,如果没有及时检查,就容易出错。 --- 4. 如何优雅地处理这种错误? 既然知道了问题所在,接下来就是解决问题的时候啦!别急,咱们可以从以下几个方面入手: 4.1 检查与验证 首先,最直接的办法就是仔细检查你的JSON数据。如果怀疑有问题,可以使用在线工具进行验证。比如著名的[JSONLint](https://jsonlint.com/),它可以帮你快速找出格式错误的地方。 4.2 使用正确的编辑器 选择一款适合的代码编辑器也很重要。像VS Code这样的工具不仅支持语法高亮,还能实时检测JSON格式是否正确。如果你发现等号突然冒出来,编辑器通常会立即提醒你。 4.3 编写自动化测试 对于经常需要处理JSON数据的项目,建议编写一些自动化测试脚本来确保数据格式无误。这样即使出现错误,也能第一时间发现并修复。 示例代码:简单的JSON验证函数 python import json def validate_json(data): try: json.loads(data) print("JSON is valid!") except ValueError as e: print(f"Invalid JSON: {e}") 测试用例 valid_json = '{"name": "Alice", "age": 25}' invalid_json = '{"name=Alice", "age=25"}' validate_json(valid_json) 输出: JSON is valid! validate_json(invalid_json) 输出: Invalid JSON: Expecting property name enclosed in double quotes: line 1 column 2 (char 1) --- 5. 总结 保持警惕,远离坑点 好了,今天的分享就到这里啦!通过这篇文章,希望大家对JSON解析中的冒号变等号问题有了更深刻的认识。嘿,听好了,这事儿可别小瞧了!哪怕就是一个不起眼的小标点,都有可能让整套系统“翻车”。细节这东西啊,就像是搭积木,你要是漏掉一块或者放歪了,那整个塔就悬乎了。所以呀,千万别觉得小地方无所谓,它们往往是关键中的关键! 最后,我想说的是,学习编程的过程就是不断踩坑又爬出来的旅程。遇到问题不可怕,可怕的是我们不去面对它。只要多加练习,多积累经验,相信每个人都能成为高手!加油吧,小伙伴们! 如果你还有其他疑问,欢迎随时来找我讨论哦~咱们下次再见啦!
2025-03-31 16:18:15
12
半夏微凉
Beego
...的格式(如 ini、json 或 yaml)。文中提到的配置文件是 Beego 框架使用的 ini 格式,包含键值对的形式定义各种配置项。例如,appname 和 port 分别定义了应用名称和监听端口号。配置文件的正确性和完整性直接影响程序的运行状态,因此需要严格检查其格式和内容。Beego 提供了专门的方法来加载和解析配置文件,确保程序能够顺利读取必要的参数。 日志记录 , 日志记录是指将程序运行过程中的重要信息(如错误、警告或调试信息)保存到文件或输出到控制台的过程。文中提到的日志记录主要用于监控配置文件加载是否成功。通过使用 Beego 提供的日志模块,开发者可以设置日志的格式和级别,例如记录日期、时间和错误发生的具体位置。当配置文件加载失败时,日志会输出详细的错误信息,帮助开发者快速定位问题。这种机制对于复杂系统的维护和故障排查至关重要,能够显著提高开发效率。
2025-04-13 15:33:12
24
桃李春风一杯酒
转载文章
...发现,也是这里提到的原因。其中,关于存在漏洞的代码,作者表述: 我最初认为,这些问题是libvnc开发者自己代码中的错误,但看起来并非如此。其中有一些(如CoRRE数据处理函数中的堆缓冲区溢出),出现在AT&T实验室1999年的代码中,而后被很多软件开发者原样复制(在Github上搜索一下HandleCoRREBPP函数,你就知道),LibVNC和TightVNC也是如此。 为了证实,翻阅了这部分代码,确实在其中数据处理相关代码文件看到了剑桥和AT&T实验室的文件头GPL声明注释,中国菜刀 这证实这些文件是直接从最初剑桥实验室版本VNC移植过来的,且使用方式是 直接代码包含,而非独立库引用方式。在官方开源发布并停止更新后,LibVNC使用的这部分代码基本没有改动——除了少数变量命名方式的统一,以及本次漏洞修复。通过搜索,我找到了2000年发布的相关代码文件,确认这些文件与LibVNC中引入的原始版本一致。 另外,Pavel同时反馈了TightVNC中相同的问题。TightVNC与LibVNC没有继承和直接引用关系,但上述VNC代码同样被TightVNC使用,问题的模式不约而同。Pavel测试发现在Ubuntu最新版本TightVNC套件(1.3.10版本)中同样存在该问题,上报给当前软件所有者GlavSoft公司,但对方声称目前精力放在不受GPL限制的TightVNC 2.x版本开发中,对开源的1.x版本漏洞代码“可能会进行修复”。看起来,这个问题被踢给了各大Linux发行版社区来焦虑了——如果他们愿意接锅。 问题思考 在披露邮件中,Pavel认为,这些代码bug“如此明显,让人无法相信之前没被人发现过……也许是因为某些特殊理由才始终没得到修复”。 事实上,我们都知道目前存在一些对开源基础软件进行安全扫描的大型项目,例如Google的OSS;同时,仍然存活的开源项目也越来越注重自身代码发布前的安全扫描,Fortify、Coverity的扫描也成为很多项目和平台的标配。在这样一些眼睛注视下,为什么还有这样的问题?我认为就这个具体事例来说,可能有如下两个因素: ·上游已死。仍然在被维护的代码,存在版本更迭,也存在外界的持续关注、漏洞报告和修复、开发的迭代,对于负责人的开发者,持续跟进、评估、同步代码的改动是可能的。但是一旦一份代码走完了生命周期,就像一段史实一样会很少再被改动。 ·对第三方上游代码的无条件信任。我们很多人都有过基础组件、中间件的开发经历,不乏有人使用Coverity开启全部规则进行代码扫描、严格修复所有提示的问题甚至编程规范warning;报告往往很长,其中也包括有源码形式包含的第三方代码中的问题。但是,我们一方面倾向于认为这些被广泛使用的代码不应存在问题(不然早就被人挖过了),一方面考虑这些引用的代码往往是组件或库的形式被使用,应该有其上下文才能认定是否确实有可被利用的漏洞条件,现在单独扫描这部分代码一般出来的都是误报。所以这些代码的问题都容易被忽视。 但是透过这个具体例子,再延伸思考相关的实践,这里最根本的问题可以总结为一个模式: 复制粘贴风险。复制粘贴并不简单意味着剽窃,实际是当前软件领域、互联网行业发展的基础模式,但其中有一些没人能尝试解决的问题: ·在传统代码领域,如C代码中,对第三方代码功能的复用依赖,往往通过直接进行库的引入实现,第三方代码独立而完整,也较容易进行整体更新;这是最简单的情况,只需要所有下游使用者保证仅使用官方版本,跟进官方更新即可;但在实践中很难如此贯彻,这是下节讨论的问题。 ·有些第三方发布的代码,模式就是需要被源码形式包含到其他项目中进行统一编译使用(例如腾讯的开源Json解析库RapidJSON,就是纯C++头文件形式)。在开源领域有如GPL等规约对此进行规范,下游开发者遵循协议,引用代码,强制或可选地显式保留其GPL声明,可以进行使用和更改。这样的源码依赖关系,结合规范化的changelog声明代码改动,侧面也是为开发过程中跟进考虑。但是一个成型的产品,比如企业自有的服务端底层产品、中间件,新版本的发版更新是复杂的过程,开发者在旧版本仍然“功能正常”的情况下往往倾向于不跟进新版本;而上游代码如果进行安全漏洞修复,通常也都只在其最新版本代码中改动,安全修复与功能迭代并存,如果没有类似Linux发行版社区的努力,旧版本代码完全没有干净的安全更新patch可用。 ·在特定场景下,有些开发实践可能不严格遵循开源代码协议限定,引入了GPL等协议保护的代码而不做声明(以规避相关责任),丢失了引入和版本的信息跟踪;在另一些场景下,可能存在对开源代码进行大刀阔斧的修改、剪裁、定制,以符合自身业务的极端需求,但是过多的修改、人员的迭代造成与官方代码严重的失同步,丧失可维护性。 ·更一般的情况是,在开发中,开发者个体往往心照不宣的存在对网上代码文件、代码片段的复制-粘贴操作。被参考的代码,可能有上述的开源代码,也可能有各种Github作者练手项目、技术博客分享的代码片段、正式开源项目仅用来说明用法的不完备示例代码。这些代码的引入完全无迹可寻,即便是作者自己也很难解释用了什么。这种情况下,上面两条认定的那些与官方安全更新失同步的问题同样存在,且引入了独特的风险:被借鉴的代码可能只是原作者随手写的、仅仅是功能成立的片段,甚至可能是恶意作者随意散布的有安全问题的代码。由此,问题进入了最大的发散空间。 在Synopsys下BLACKDUCK软件之前发布的《2018 Open Source Security and Risk Analysis Report》中分析,96%的应用中包含有开源组件和代码,开源代码在应用全部代码中的占比约为57%,78%的应用中在引用的三方开源代码中存在历史漏洞。也就是说,现在互联网上所有厂商开发的软件、应用,其开发人员自己写的代码都是一少部分,多数都是借鉴来的。而这还只是可统计、可追溯的;至于上面提到的非规范的代码引用,如果也纳入进来考虑,三方代码占应用中的比例会上升到多少?曾经有分析认为至少占80%,我们只期望不会更高。 Ⅱ. 从碎片到乱刃:OpenSSH在野后门一览 在进行基础软件梳理时,回忆到反病毒安全软件提供商ESET在2018年十月发布的一份白皮书《THE DARK SIDE OF THE FORSSHE: A landscape of OpenSSH backdoors》。其站在一个具有广泛用户基础的软件提供商角度,给出了一份分析报告,数据和结论超出我们对于当前基础软件使用全景的估量。以下以我的角度对其中一方面进行解读。 一些必要背景 SSH的作用和重要性无需赘言;虽然我们站在传统互联网公司角度,可以认为SSH是通往生产服务器的生命通道,但当前多样化的产业环境已经不止于此(如之前libssh事件中,不幸被我言中的,SSH在网络设备、IoT设备上(如f5)的广泛使用)。 OpenSSH是目前绝大多数SSH服务端的基础软件,有完备的开发团队、发布规范、维护机制,本身是靠谱的。如同绝大多数基础软件开源项目的做法,OpenSSH对漏洞有及时的响应,针对最新版本代码发出安全补丁,但是各大Linux发行版使用的有各种版本的OpenSSH,这些社区自行负责将官方开发者的安全补丁移植到自己系统搭载的低版本代码上。天空彩 白皮书披露的现状 如果你是一个企业的运维管理人员,需要向企业生产服务器安装OpenSSH或者其它基础软件,最简单的方式当然是使用系统的软件管理安装即可。但是有时候,出于迁移成本考虑,可能企业需要在一个旧版本系统上,使用较新版本的OpenSSL、OpenSSH等基础软件,这些系统不提供,需要自行安装;或者需要一个某有种特殊特性的定制版本。这时,可能会选择从某些rpm包集中站下载某些不具名第三方提供的现成的安装包,或者下载非官方的定制化源码本地编译后安装,总之从这里引入了不确定性。 这种不确定性有多大?我们粗估一下,似乎不应成为问题。但这份白皮书给我们看到了鲜活的数据。 ESET研究人员从OpenSSH的一次历史大规模Linux服务端恶意软件Windigo中获得启示,采用某种巧妙的方式,面向在野的服务器进行数据采集,主要是系统与版本、安装的OpenSSH版本信息以及服务端程序文件的一个特殊签名。整理一个签名白名单,包含有所有能搜索到的官方发布二进制版本、各大Linux发行版本各个版本所带的程序文件版本,将这些标定为正常样本进行去除。最终结论是: ·共发现了几百个非白名单版本的OpenSSH服务端程序文件ssh和sshd; ·分析这些样本,将代码部分完全相同,仅仅是数据和配置不同的合并为一类,且分析判定确认有恶意代码的,共归纳为 21个各异的恶意OpenSSH家族; ·在21个恶意家族中,有12个家族在10月份时完全没有被公开发现分析过;而剩余的有一部分使用了历史上披露的恶意代码样本,甚至有源代码; ·所有恶意样本的实现,从实现复杂度、代码混淆和自我保护程度到代码特征有很大跨度的不同,但整体看,目的以偷取用户凭证等敏感信息、回连外传到攻击者为主,其中有的攻击者回连地址已经存在并活跃数年之久; ·这些后门的操控者,既有传统恶意软件黑产人员,也有APT组织; ·所有恶意软件或多或少都在被害主机上有未抹除的痕迹。ESET研究者尝试使用蜜罐引诱出攻击者,但仍有许多未解之谜。这场对抗,仍未取胜。 白皮书用了大篇幅做技术分析报告,此处供细节分析,不展开分析,以下为根据恶意程序复杂度描绘的21个家族图谱: 问题思考 问题引入的可能渠道,我在开头进行了一点推测,主要是由人的原因切入的,除此以外,最可能的是恶意攻击者在利用各种方法入侵目标主机后,主动替换了目标OpenSSH为恶意版本,从而达成攻击持久化操作。但是这些都是止血的安全运维人员该考虑的事情;关键问题是,透过表象,这显露了什么威胁形式? 这个问题很好回答,之前也曾经反复说过:基础软件碎片化。 如上一章节简单提到,在开发过程中有各种可能的渠道引入开发者不完全了解和信任的代码;在运维过程中也是如此。二者互相作用,造成了软件碎片化的庞杂现状。在企业内部,同一份基础软件库,可能不同的业务线各自定制一份,放到企业私有软件仓库源中,有些会有人持续更新供自己产品使用,有些由系统软件基础设施维护人员单独维护,有些则可能是开发人员临时想起来上传的,他们自己都不记得;后续用到的这个基础软件的开发和团队,在这个源上搜索到已有的库,很大概率会倾向于直接使用,不管来源、是否有质量背书等。长此以往问题会持续发酵。而我们开最坏的脑洞,是否可能有黑产人员入职到内部,提交个恶意基础库之后就走人的可能?现行企业安全开发流程中审核机制的普遍缺失给这留下了空位。 将源码来源碎片化与二进制使用碎片化并起来考虑,我们不难看到一个远远超过OpenSSH事件威胁程度的图景。但这个问题不是仅仅靠开发阶段规约、运维阶段规范、企业内部管控、行业自查、政府监管就可以根除的,最大的问题归根结底两句话: 不可能用一场战役对抗持续威胁;不可能用有限分析对抗无限未知。 Ⅲ. 从自信到自省:RHEL、CentOS backport版本BIND漏洞 2018年12月20日凌晨,在备战冬至的软件供应链安全大赛决赛时,我注意到漏洞预警平台捕获的一封邮件。但这不是一个漏洞初始披露邮件,而是对一个稍早已披露的BIND在RedHat、CentOS发行版上特定版本的1day漏洞CVE-2018-5742,由BIND的官方开发者进行额外信息澄(shuǎi)清(guō)的邮件。 一些必要背景 关于BIND 互联网的一个古老而基础的设施是DNS,这个概念在读者不应陌生。而BIND“是现今互联网上最常使用的DNS软件,使用BIND作为服务器软件的DNS服务器约占所有DNS服务器的九成。BIND现在由互联网系统协会负责开发与维护参考。”所以BIND的基础地位即是如此,因此也一向被大量白帽黑帽反复测试、挖掘漏洞,其开发者大概也一直处在紧绷着应对的处境。 关于ISC和RedHat 说到开发者,上面提到BIND的官方开发者是互联网系统协会(ISC)。ISC是一个老牌非营利组织,目前主要就是BIND和DHCP基础设施的维护者。而BIND本身如同大多数历史悠久的互联网基础开源软件,是4个UCB在校生在DARPA资助下于1984年的实验室产物,直到2012年由ISC接管。 那么RedHat在此中是什么角色呢?这又要提到我之前提到的Linux发行版和自带软件维护策略。Red Hat Enterprise Linux(RHEL)及其社区版CentOS秉持着稳健的软件策略,每个大的发行版本的软件仓库,都只选用最必要且质量久经时间考验的软件版本,哪怕那些版本实在是老掉牙。这不是一种过分的保守,事实证明这种策略往往给RedHat用户在最新漏洞面前提供了保障——代码总是跑得越少,潜在漏洞越多。 但是这有两个关键问题。一方面,如果开源基础软件被发现一例有历史沿革的代码漏洞,那么官方开发者基本都只为其最新代码负责,在当前代码上推出修复补丁。另一方面,互联网基础设施虽然不像其上的应用那样爆发性迭代,但依然持续有一些新特性涌现,其中一些是必不可少的,但同样只在最新代码中提供。两个刚需推动下,各Linux发行版对长期支持版本系统的软件都采用一致的策略,即保持其基础软件在一个固定的版本,但对于这些版本软件的最新漏洞、必要的最新软件特性,由发行版维护者将官方开发者最新代码改动“向后移植”到旧版本代码中,即backport。这就是基础软件的“官宣”碎片化的源头。 讲道理,Linux发行版维护者与社区具有比较靠谱的开发能力和监督机制,backport又基本就是一些复制粘贴工作,应当是很稳当的……但真是如此吗? CVE-2018-5742漏洞概况 CVE-2018-5742是一个简单的缓冲区溢出类型漏洞,官方评定其漏洞等级moderate,认为危害不大,漏洞修复不积极,披露信息不多,也没有积极给出代码修复patch和新版本rpm包。因为该漏洞仅在设置DEBUG_LEVEL为10以上才会触发,由远程攻击者构造畸形请求造成BIND服务崩溃,在正常的生产环境几乎不可能具有危害,RedHat官方也只是给出了用户自查建议。 这个漏洞只出现在RHEL和CentOS版本7中搭载的BIND 9.9.4-65及之后版本。RedHat同ISC的声明中都证实,这个漏洞的引入原因,是RedHat在尝试将BIND 9.11版本2016年新增的NTA机制向后移植到RedHat 7系中固定搭载的BIND 9.9版本代码时,偶然的代码错误。NTA是DNS安全扩展(DNSSEC)中,用于在特定域关闭DNSSEC校验以避免不必要的校验失败的机制;但这个漏洞不需要对NTA本身有进一步了解。 漏洞具体分析 官方没有给出具体分析,但根据CentOS社区里先前有用户反馈的bug,我得以很容易还原漏洞链路并定位到根本原因。 若干用户共同反馈,其使用的BIND 9.9.4-RedHat-9.9.4-72.el7发生崩溃(coredump),并给出如下的崩溃时调用栈backtrace: 这个调用过程的逻辑为,在9 dns_message_logfmtpacket函数判断当前软件设置是否DEBUG_LEVEL大于10,若是,对用户请求数据包做日志记录,先后调用8 dns_message_totext、7 dns_message_sectiontotext、6 dns_master_rdatasettotext、5 rdataset_totext将请求进行按协议分解分段后写出。 由以上关键调用环节,联动RedHat在9.9.4版本BIND源码包中关于引入NTA特性的源码patch,进行代码分析,很快定位到问题产生的位置,在上述backtrace中的5,masterdump.c文件rdataset_totext函数。漏洞相关代码片段中,RedHat进行backport后,这里引入的代码为: 这里判断对于请求中的注释类型数据,直接通过isc_buffer_putstr宏对缓存进行操作,在BIND工程中自定义维护的缓冲区结构对象target上,附加一字节字符串(一个分号)。而漏洞就是由此产生:isc_buffer_putstr中不做缓冲区边界检查保证,这里在缓冲区已满情况下将造成off-by-one溢出,并触发了缓冲区实现代码中的assertion。 而ISC上游官方版本的代码在这里是怎么写的呢?找到ISC版本BIND 9.11代码,这里是这样的: 这里可以看到,官方代码在做同样的“附加一个分号”这个操作时,审慎的使用了做缓冲区剩余空间校验的str_totext函数,并额外做返回值成功校验。而上述提到的str_totext函数与RETERR宏,在移植版本的masterdump.c中,RedHat开发者也都做了保留。但是,查看代码上下文发现,在RedHat开发者进行代码移植过程中,对官方代码进行了功能上的若干剪裁,包括一些细分数据类型记录的支持;而这里对缓冲区写入一字节,也许开发者完全没想到溢出的可能,所以自作主张地简化了代码调用过程。 问题思考 这个漏洞本身几乎没什么危害,但是背后足以引起思考。 没有人在“借”别人代码时能不出错 不同于之前章节提到的那种场景——将代码文件或片段复制到自己类似的代码上下文借用——backport作为一种官方且成熟的做法,借用的代码来源、粘贴到的代码上下文,是具有同源属性的,而且开发者一般是追求稳定性优先的社区开发人员,似乎质量应该有足够保障。但是这里的关键问题是:代码总要有一手、充分的语义理解,才能有可信的使用保障;因此,只要是处理他人的代码,因为不够理解而错误使用的风险,只可能减小,没办法消除。 如上分析,本次漏洞的产生看似只是做代码移植的开发者“自作主张”之下“改错了”。但是更广泛且可能的情况是,原始开发者在版本迭代中引入或更新大量基础数据结构、API的定义,并用在新的特性实现代码中;而后向移植开发人员仅需要最小规模的功能代码,所以会对增量代码进行一定规模的修改、剪裁、还原,以此适应旧版本基本代码。这些过程同样伴随着第三方开发人员不可避免的“望文生义”,以及随之而来的风险。后向移植操作也同样助长了软件碎片化过程,其中每一个碎片都存在这样的问题;每一个碎片在自身生命周期也将有持续性影响。 多级复制粘贴无异于雪上加霜 这里简单探讨的是企业通行的系统和基础软件建设实践。一些国内外厂商和社区发布的定制化Linux发行版,本身是有其它发行版,如CentOS特定版本渊源的,在基础软件上即便同其上游发行版最新版本间也存在断层滞后。RedHat相对于基础软件开发者之间已经隔了一层backport,而我们则人为制造了二级风险。 在很多基础而关键的软件上,企业系统基础设施的维护者出于与RedHat类似的初衷,往往会决定自行backport一份拷贝;通过早年心脏滴血事件的洗礼,即暴露出来OpenSSL一个例子。无论是需要RHEL还没来得及移植的新版本功能特性,还是出于对特殊使用上下文场景中更高执行效率的追求,企业都可能自行对RHEL上基础软件源码包进行修改定制重打包。这个过程除了将风险幂次放大外,也进一步加深了代码的不可解释性(包括基础软件开发人员流动性带来的不可解释)。 Ⅳ. 从武功到死穴:从systemd-journald信息泄露一窥API误用 1月10日凌晨两点,漏洞预警平台爬收取一封漏洞披露邮件。披露者是Qualys,那就铁定是重型发布了。最后看披露漏洞的目标,systemd?这就非常有意思了。 一些必要背景 systemd是什么,不好简单回答。Linux上面软件命名,习惯以某软件名后带个‘d’表示后台守护管理程序;所以systemd就可以说是整个系统的看守吧。而即便现在描述了systemd是什么,可能也很快会落伍,因为其初始及核心开发者Lennart Poettering(供职于Red Hat)描述它是“永无开发完结完整、始终跟进技术进展的、统一所有发行版无止境的差异”的一种底层软件。笼统讲有三个作用:中央化系统及设置管理;其它软件开发的基础框架;应用程序和系统内核之间的胶水。如今几乎所有Linux发行版已经默认提供systemd,包括RHEL/CentOS 7及后续版本。总之很基础、很底层、很重要就对了。systemd本体是个主要实现init系统的框架,但还有若干关键组件完成其它工作;这次被爆漏洞的是其journald组件,是负责系统事件日志记录的看守程序。 额外地还想简单提一句Qualys这个公司。该公司创立于1999年,官方介绍为信息安全与云安全解决方案企业,to B的安全业务非常全面,有些也是国内企业很少有布局的方面;例如上面提到的涉及碎片化和代码移植过程的历史漏洞移动,也在其漏洞管理解决方案中有所体现。但是我们对这家公司粗浅的了解来源于其安全研究团队近几年的发声,这两年间发布过的,包括有『stack clash』、『sudo get_tty_name提权』、『OpenSSH信息泄露与堆溢出』、『GHOST:glibc gethostbyname缓冲区溢出』等大新闻(仅截至2017年年中)。从中可见,这个研究团队专门啃硬骨头,而且还总能开拓出来新的啃食方式,往往爆出来一些别人没想到的新漏洞类型。从这个角度,再联想之前刷爆朋友圈的《安全研究者的自我修养》所倡导的“通过看历史漏洞、看别人的最新成果去举一反三”的理念,可见差距。 CVE-2018-16866漏洞详情 这次漏洞披露,打包了三个漏洞: ·16864和16865是内存破坏类型 ·16866是信息泄露 ·而16865和16866两个漏洞组和利用可以拿到root shell。 漏洞分析已经在披露中写的很详细了,这里不复述;而针对16866的漏洞成因来龙去脉,Qualys跟踪的结果留下了一点想象和反思空间,我们来看一下。 漏洞相关代码片段是这样的(漏洞修复前): 读者可以先肉眼过一遍这段代码有什么问题。实际上我一开始也没看出来,向下读才恍然大悟。 这段代码中,外部信息输入通过buf传入做记录处理。输入数据一般包含有空白字符间隔,需要分隔开逐个记录,有效的分隔符包括空格、制表符、回车、换行,代码中将其写入常量字符串;在逐字符扫描输入数据字符串时,将当前字符使用strchr在上述间隔符字符串中检索是否匹配,以此判断是否为间隔符;在240行,通过这样的判断,跳过记录单元字符串的头部连续空白字符。 但是问题在于,strchr这个极其基础的字符串处理函数,对于C字符串终止字符'\0'的处理上有个坑:'\0'也被认为是被检索字符串当中的一个有效字符。所以在240行,当当前扫描到的字符为字符串末尾的NULL时,strchr返回的是WHITESPACE常量字符串的终止位置而非NULL,这导致了越界。 看起来,这是一个典型的问题:API误用(API mis-use),只不过这个被误用的库函数有点太基础,让我忍不住想是不是还会有大量的类似漏洞……当然也反思我自己写的代码是不是也有同样情况,然而略一思考就释然了——我那么笨的代码都用for循环加if判断了:) 漏洞引入和消除历史 有意思的是,Qualys研究人员很贴心地替我做了一步漏洞成因溯源,这才是单独提这个漏洞的原因。漏洞的引入是在2015年的一个commit中: 在GitHub中,定位到上述2015年的commit信息,这里commit的备注信息为: journald: do not strip leading whitespace from messages. Keep leading whitespace for compatibility with older syslog implementations. Also useful when piping formatted output to the logger command. Keep removing trailing whitespace. OK,看起来是一个兼容性调整,对记录信息不再跳过开头所有连续空白字符,只不过用strchr的简洁写法比较突出开发者精炼的开发风格(并不),说得过去。 之后在2018年八月的一个当时尚未推正式版的另一次commit中被修复了,先是还原成了ec5ff4那次commit之前的写法,然后改成了加校验的方式: 虽然Qualys研究者认为上述的修改是“无心插柳”的改动,但是在GitHub可以看到,a6aadf这次commit是因为有外部用户反馈了输入数据为单个冒号情况下journald堆溢出崩溃的issue,才由开发者有目的性地修复的;而之后在859510这个commit再次改动回来,理由是待记录的消息都是使用单个空格作为间隔符的,而上一个commit粗暴地去掉了这种协议兼容性特性。 如果没有以上纠结的修改和改回历史,也许我会倾向于怀疑,在最开始漏洞引入的那个commit,既然改动代码没有新增功能特性、没有解决什么问题(毕竟其后三年,这个改动的代码也没有被反映issue),也并非出于代码规范等考虑,那么这么轻描淡写的一次提交,难免有人为蓄意引入漏洞的嫌疑。当然,看到几次修复的原因,这种可能性就不大了,虽然大家仍可以保留意见。但是抛开是否人为这个因素,单纯从代码的漏洞成因看,一个传统但躲不开的问题仍值得探讨:API误用。 API误用:程序员何苦为难程序员 如果之前的章节给读者留下了我反对代码模块化和复用的印象,那么这里需要正名一下,我们认可这是当下开发实践不可避免的趋势,也增进了社会开发速度。而API的设计决定了写代码和用代码的双方“舒适度”的问题,由此而来的API误用问题,也是一直被当做单纯的软件工程课题讨论。在此方面个人并没有什么研究,自然也没办法系统地给出分类和学术方案,只是谈一下自己的经验和想法。 一篇比较新的学术文章总结了API误用的研究,其中一个独立章节专门分析Java密码学组件API误用的实际,当中引述之前论文认为,密码学API是非常容易被误用的,比如对期望输入数据(数据类型,数据来源,编码形式)要求的混淆,API的必需调用次序和依赖缺失(比如缺少或冗余多次调用了初始化函数、主动资源回收函数)等。凑巧在此方面我有一点体会:曾经因为业务方需要,需要使用C++对一个Java的密码基础中间件做移植。Java对密码学组件支持,有原生的JDK模块和权威的BouncyCastle包可用;而C/C++只能使用第三方库,考虑到系统平台最大兼容和最小代码量,使用Linux平台默认自带的OpenSSL的密码套件。但在开发过程中感受到了OpenSSL满满的恶意:其中的API设计不可谓不反人类,很多参数没有明确的说明(比如同样是表示长度的函数参数,可能在不同地方分别以字节/比特/分组数为计数单位);函数的线程安全没有任何解释标注,需要自行试验;不清楚函数执行之后,是其自行做了资源释放还是需要有另外API做gc,不知道资源释放操作时是否规规矩矩地先擦除后释放……此类问题不一而足,导致经过了漫长的测试之后,这份中间件才提供出来供使用。而在业务场景中,还会存在比如其它语言调用的情形,这些又暴露出来OpenSSL API误用的一些完全无从参考的问题。这一切都成为了噩梦;当然这无法为我自己开解是个不称职开发的指责,但仅就OpenSSL而言其API设计之恶劣也是始终被人诟病的问题,也是之后其他替代者宣称改进的地方。 当然,问题是上下游都脱不了干系的。我们自己作为高速迭代中的开发人员,对于二方、三方提供的中间件、API,又有多少人能自信地说自己仔细、认真地阅读过开发指南和API、规范说明呢?做过通用产品技术运营的朋友可能很容易理解,自己产品的直接用户日常抛出不看文档的愚蠢问题带来的困扰。对于密码学套件,这个问题还好办一些,毕竟如果在没有背景知识的情况下对API望文生义地一通调用,绝大多数情况下都会以抛异常形式告终;但还是有很多情况,API误用埋下的是长期隐患。 不是所有API误用情形最终都有机会发展成为可利用的安全漏洞,但作为一个由人的因素引入的风险,这将长期存在并困扰软件供应链(虽然对安全研究者、黑客与白帽子是很欣慰的事情)。可惜,传统的白盒代码扫描能力,基于对代码语义的理解和构建,但是涉及到API则需要预先的抽象,这一点目前似乎仍然是需要人工干预的事情;或者轻量级一点的方案,可以case by case地分析,为所有可能被误用的API建模并单独扫描,这自然也有很强局限性。在一个很底层可信的开发者还对C标准库API存在误用的现实内,我们需要更多的思考才能说接下来的解法。 Ⅴ. 从规则到陷阱:NASA JIRA误配置致信息泄露血案 软件的定义包括了代码组成的程序,以及相关的配置、文档等。当我们说软件的漏洞、风险时,往往只聚焦在其中的代码中;关于软件供应链安全风险,我们的比赛、前面分析的例子也都聚焦在了代码的问题;但是真正的威胁都来源于不可思议之处,那么代码之外有没有可能存在来源于上游的威胁呢?这里就借助实例来探讨一下,在“配置”当中可能栽倒的坑。 引子:发不到500英里以外的邮件? 让我们先从一个轻松愉快的小例子引入。这个例子初见于Linux中国的一篇译文。 简单说,作者描述了这么一个让人啼笑皆非的问题:单位的邮件服务器发送邮件,发送目标距离本地500英里范围之外的一律失败,邮件就像悠悠球一样只能飞出一定距离。这个问题本身让描述者感到尴尬,就像一个技术人员被老板问到“为什么从家里笔记本上Ctrl-C后不能在公司台式机上Ctrl-V”一样。 经过令人窒息的分析操作后,笔者定位到了问题原因:笔者作为负责的系统管理员,把SunOS默认安装的Senmail从老旧的版本5升级到了成熟的版本8,且对应于新版本诸多的新特性进行了对应配置,写入配置文件sendmail.cf;但第三方服务顾问在对单位系统进行打补丁升级维护时,将系统软件“升级”到了系统提供的最新版本,因此将Sendmail实际回退到了版本5,却为了软件行为一致性,原样保留了高版本使用的配置文件。但Sendmail并没有在大版本间保证配置文件兼容性,这导致很多版本5所需的配置项不存在于保留下来的sendmail.cf文件中,程序按默认值0处理;最终引起问题的就是,邮件服务器与接收端通信的超时时间配置项,当取默认配置值0时,邮件服务器在1个单位时间(约3毫秒)内没有收到网络回包即认为超时,而这3毫秒仅够电信号打来回飞出500英里。 这个“故事”可能会给技术人员一点警醒,错误的配置会导致预期之外的软件行为,但是配置如何会引入软件供应链方向的安全风险呢?这就引出了下一个重磅实例。 JIRA配置错误致NASA敏感信息泄露案例 我们都听过一个事情,马云在带队考察美国公司期间问Google CEO Larry Page自视谁为竞争对手,Larry的回答是NASA,因为最优秀的工程师都被NASA的梦想吸引过去了。由此我们显然能窥见NASA的技术水位之高,这样的人才团队大概至少是不会犯什么低级错误的。 但也许需要重新定义“低级错误”……1月11日一篇技术文章披露,NASA某官网部署使用的缺陷跟踪管理系统JIRA存在错误的配置,可分别泄漏内部员工(JIRA系统用户)的全部用户名和邮件地址,以及内部项目和团队名称到公众,如下: 问题的原因解释起来也非常简单:JIRA系统的过滤器和配置面板中,对于数据可见性的配置选项分别选定为All users和Everyone时,系统管理人员想当然地认为这意味着将数据对所有“系统用户”开放查看,但是JIRA的这两个选项的真实效果逆天,是面向“任意人”开放,即不限于系统登录用户,而是任何查看页面的人员。看到这里,我不厚道地笑了……“All users”并不意味着“All ‘users’”,意不意外,惊不惊喜? 但是这种字面上把戏,为什么没有引起NASA工程师的注意呢,难道这样逆天的配置项没有在产品手册文档中加粗标红提示吗?本着为JIRA产品设计找回尊严的态度,我深入挖掘了一下官方说明,果然在Atlassian官方的一份confluence文档(看起来更像是一份增补的FAQ)中找到了相关说明: 所有未登录访客访问时,系统默认认定他们是匿名anonymous用户,所以各种权限配置中的all users或anyone显然应该将匿名用户包括在内。在7.2及之后版本中,则提供了“所有登录用户”的选项。 可以说是非常严谨且贴心了。比较讽刺的是,在我们的软件供应链安全大赛·C源代码赛季期间,我们设计圈定的恶意代码攻击目标还包括JIRA相关的敏感信息的窃取,但是却想不到有这么简单方便的方式,不动一行代码就可以从JIRA中偷走数据。 软件的使用,你“配”吗? 无论是开放的代码还是成型的产品,我们在使用外部软件的时候,都是处于软件供应链下游的消费者角色,为了要充分理解上游开发和产品的真实细节意图,需要我们付出多大的努力才够“资格”? 上一章节我们讨论过源码使用中必要细节信息缺失造成的“API误用”问题,而软件配置上的“误用”问题则复杂多样得多。从可控程度上讨论,至少有这几种因素定义了这个问题: ·软件用户对必要配置的现有文档缺少了解。这是最简单的场景,但又是完全不可避免的,这一点上我们所有有开发、产品或运营角色经验的应该都曾经体会过向不管不顾用户答疑的痛苦,而所有软件使用者也可以反省一下对所有软件的使用是否都以完整细致的文档阅读作为上手的准备工作,所以不必多说。 ·软件拥有者对配置条目缺少必要明确说明文档。就JIRA的例子而言,将NASA工程师归为上一条错误有些冤枉,而将JIRA归为这条更加合适。在边角但重要问题上的说明通过社区而非官方文档形式发布是一种不负责任的做法,但未引发安全事件的情况下还有多少这样的问题被默默隐藏呢?我们没办法要求在使用软件之前所有用户将软件相关所有文档、社区问答实现全部覆盖。这个问题范围内一个代表性例子是对配置项的默认值以及对应效果的说明缺失。 ·配置文件版本兼容性带来的误配置和安全问题。实际上,上面的SunOS Sendmail案例足以点出这个问题的存在性,但是在真实场景下,很可能不会以这么戏剧性形式出现。在企业的系统运维中,系统的版本迭代常见,但为软件行为一致性,配置的跨版本迁移是不可避免的操作;而且软件的更新迭代也不只会由系统更新推动,还有大量出于业务性能要求而主动进行的定制化升级,对于中小企业基础设施建设似乎是一个没怎么被提及过的问题。 ·配置项组合冲突问题。尽管对于单个配置项可能明确行为与影响,但是特定的配置项搭配可能造成不可预知的效果。这完全有可能是由于开发者与用户在信息不对等的情况下产生:开发者认为用户应该具有必需的背景知识,做了用户应当具备规避配置冲突能力的假设。一个例子是,对称密码算法在使用ECB、CBC分组工作模式时,从密码算法上要求输入数据长度必须是分组大小的整倍数,但如果用户搭配配置了秘钥对数据不做补齐(nopadding),则引入了非确定性行为:如果密码算法库对这种组合配置按某种默认补齐方式操作数据则会引起歧义,但如果在算法库代码层面对这种组合抛出错误则直接影响业务。 ·程序对配置项处理过程的潜在暗箱操作。这区别于简单的未文档化配置项行为,仅特指可能存在的蓄意、恶意行为。从某种意义上,上述“All users”也可以认为是这样的一种陷阱,通过浅层次暗示,引导用户做出错误且可能引起问题的配置。另一种情况是特定配置组合情况下触发恶意代码的行为,这种触发条件将使恶意代码具有规避检测的能力,且在用户基数上具有一定概率的用户命中率。当然这种情况由官方开发者直接引入的可能性很低,但是在众包开发的情况下如果存在,那么扫描方案是很难检测的。 Ⅵ. 从逆流到暗流:恶意代码溯源后的挑战 如果说前面所说的种种威胁都是面向关键目标和核心系统应该思考的问题,那么最后要抛出一个会把所有人拉进赛场的理由。除了前面所有那些在软件供应链下游被动污染受害的情况,还有一种情形:你有迹可循的代码,也许在不经意间会“反哺”到黑色产业链甚至特殊武器中;而现在研究用于对程序进行分析和溯源的技术,则会让你陷入百口莫辩的境地。 案例:黑产代码模块溯源疑云 1月29日,猎豹安全团队发布技术分析通报文章《电信、百度客户端源码疑遭泄漏,驱魔家族窃取隐私再起波澜》,矛头直指黑产上游的恶意信息窃取代码模块,认定其代码与两方产品存在微妙的关联:中国电信旗下“桌面3D动态天气”等多款软件,以及百度旗下“百度杀毒”等软件(已不可访问)。 文章中举证有三个关键点。 首先最直观的,是三者使用了相同的特征字符串、私有文件路径、自定义内部数据字段格式; 其次,在关键代码位置,三者在二进制程序汇编代码层面具有高度相似性; 最终,在一定范围的非通用程序逻辑上,三者在经过反汇编后的代码语义上显示出明显的雷同,并提供了如下两图佐证(图片来源): 文章指出的涉事相关软件已经下线,对于上述样本文件的相似度试验暂不做复现,且无法求证存在相似、疑似同源的代码在三者中占比数据。对于上述指出的代码雷同现象,猎豹安全团队认为: 我们怀疑该病毒模块的作者通过某种渠道(比如“曾经就职”),掌握有中国电信旗下部分客户端/服务端源码,并加以改造用于制作窃取用户隐私的病毒,另外在该病毒模块的代码中,我们还发现“百度”旗下部分客户端的基础调试日志函数库代码痕迹,整个“驱魔”病毒家族疑点重重,其制作传播背景愈发扑朔迷离。 这样的推断,固然有过于直接的依据(例如三款代码中均使用含有“baidu”字样的特征注册表项);但更进一步地,需要注意到,三个样本在所指出的代码位置,具有直观可见的二进制汇编代码结构的相同,考虑到如果仅仅是恶意代码开发者先逆向另外两份代码后借鉴了代码逻辑,那么在面临反编译、代码上下文适配重构、跨编译器和选项的编译结果差异等诸多不确定环节,仍能保持二进制代码的雷同,似乎确实是只有从根本上的源代码泄漏(抄袭)且保持相同的开发编译环境才能成立。 但是我们却又无法做出更明确的推断。这一方面当然是出于严谨避免过度解读;而从另一方面考虑,黑产代码的一个关键出发点就是“隐藏自己”,而这里居然如此堂而皇之地照搬了代码,不但没有进行任何代码混淆、变形,甚至没有抹除疑似来源的关键字符串,如果将黑产视为智商在线的对手,那这里背后是否有其它考量,就值得琢磨了。 代码的比对、分析、溯源技术水准 上文中的安全团队基于大量样本和粗粒度比对方法,给出了一个初步的判断和疑点。那么是否有可能获得更确凿的分析结果,来证实或证伪同源猜想呢? 无论是源代码还是二进制,代码比对技术作为一种基础手段,在软件供应链安全分析上都注定仍然有效。在我们的软件供应链安全大赛期间,针对PE二进制程序类型的题目,参赛队伍就纷纷采用了相关技术手段用于目标分析,包括:同源性分析,用于判定与目标软件相似度最高的同软件官方版本;细粒度的差异分析,用于尝试在忽略编译差异和特意引入的混淆之外,定位特意引入的恶意代码位置。当然,作为比赛中针对性的应对方案,受目标和环境引导约束,这些方法证明了可行性,却难以保证集成有最新技术方案。那么做一下预言,在不计入情报辅助条件下,下一代的代码比对将能够到达什么水准? 这里结合近一年和今年内,已发表和未发表的学术领域顶级会议的相关文章来简单展望: ·针对海量甚至全量已知源码,将可以实现准确精细化的“作者归属”判定。在ACM CCS‘18会议上曾发表的一篇文章《Large-Scale and Language-Oblivious Code Authorship Identification》,描述了使用RNN进行大规模代码识别的方案,在圈定目标开发者,并预先提供每个开发者的5-7份已知的代码文件后,该技术方案可以很有效地识别大规模匿名代码仓库中隶属于每个开发者的代码:针对1600个Google Code Jam开发者8年间的所有代码可以实现96%的成功识别率,而针对745个C代码开发者于1987年之后在GitHub上面的全部公开代码仓库,识别率也高达94.38%。这样的结果在当下的场景中,已经足以实现对特定人的代码识别和跟踪(例如,考虑到特定开发人员可能由于编码习惯和规范意识,在时间和项目跨度上犯同样的错误);可以预见,在该技术方向上,完全可以期望摆脱特定已知目标人的现有数据集学习的过程,并实现更细粒度的归属分析,例如代码段、代码行、提交历史。 ·针对二进制代码,更准确、更大规模、更快速的代码主程序分析和同源性匹配。近年来作为一项程序分析基础技术研究,二进制代码相似性分析又重新获得了学术界和工业界的关注。在2018年和2019(已录用)的安全领域四大顶级会议上,每次都会有该方向最新成果的展示,如S&P‘2019上录用的《Asm2Vec: Boosting Static Representation Robustness for Binary Clone Search against Code Obfuscation and Compiler Optimization》,实现无先验知识的条件下的最优汇编代码级别克隆检测,针对漏洞库的漏洞代码检测可实现0误报、100%召回。而2018年北京HITB会议上,Google Project Zero成员、二进制比对工具BinDiff原始作者Thomas Dullien,探讨了他借用改造Google自家SimHash算法思想,用于针对二进制代码控制流图做相似性检测的尝试和阶段结果;这种引入规模数据处理的思路,也可期望能够在目前其他技术方案大多精细化而低效的情况下,为高效、快速、大规模甚至全量代码克隆检测勾出未来方案。 ·代码比对方案对编辑、优化、变形、混淆的对抗。近年所有技术方案都以对代码“变种”的检测有效性作为关键衡量标准,并一定程度上予以保证。上文CCS‘18论文工作,针对典型源代码混淆(如Tigress)处理后的代码,大规模数据集上可有93.42%的准确识别率;S&P‘19论文针对跨编译器和编译选项、业界常用的OLLVM编译时混淆方案进行试验,在全部可用的混淆方案保护之下的代码仍然可以完成81%以上的克隆检测。值得注意的是以上方案都并非针对特定混淆方案单独优化的,方法具有通用价值;而除此以外还有很多针对性的的反混淆研究成果可用;因此,可以认为在采用常规商用代码混淆方案下,即便存在隐藏内部业务逻辑不被逆向的能力,但仍然可以被有效定位代码复用和开发者自然人。 代码溯源技术面前的“挑战” 作为软件供应链安全的独立分析方,健壮的代码比对技术是决定性的基石;而当脑洞大开,考虑到行业的发展,也许以下两种假设的情景,将把每一个“正当”的产品、开发者置于尴尬的境地。 代码仿制 在本章节引述的“驱魔家族”代码疑云案例中,黑产方面通过某种方式获得了正常代码中,功能逻辑可以被自身复用的片段,并以某种方法将其在保持原样的情况下拼接形成了恶意程序。即便在此例中并非如此,但这却暴露了隐忧:将来是不是有这种可能,我的正常代码被泄漏或逆向后出现在恶意软件中,被溯源后扣上黑锅? 这种担忧可能以多种渠道和形式成为现实。 从上游看,内部源码被人为泄漏是最简单的形式(实际上,考虑到代码的完整生命周期似乎并没有作为企业核心数据资产得到保护,目前实质上有没有这样的代码在野泄漏还是个未知数),而通过程序逆向还原代码逻辑也在一定程度上可获取原始代码关键特征。 从下游看,则可能有多种方式将恶意代码伪造得像正常代码并实现“碰瓷”。最简单地,可以大量复用关键代码特征(如字符串,自定义数据结构,关键分支条件,数据记录和交换私有格式等)。考虑到在进行溯源时,分析者实际上不需要100%的匹配度才会怀疑,因此仅仅是仿造原始程序对于第三方公开库代码的特殊定制改动,也足以将公众的疑点转移。而近年来类似自动补丁代码搜索生成的方案也可能被用来在一份最终代码中包含有二方甚至多方原始代码的特征和片段。 基于开发者溯源的定点渗透 既然在未来可能存在准确将代码与自然人对应的技术,那么这种技术也完全可能被黑色产业利用。可能的忧患包括强针对性的社会工程,结合特定开发者历史代码缺陷的漏洞挖掘利用,联动第三方泄漏人员信息的深层渗透,等等。这方面暂不做联想展开。 〇. 没有总结 作为一场旨在定义“软件供应链安全”威胁的宣言,阿里安全“功守道”大赛将在后续给出详细的分解和总结,其意义价值也许会在一段时间之后才能被挖掘。 但是威胁的现状不容乐观,威胁的发展不会静待;这一篇随笔仅仅挑选六个侧面做摘录分析,可即将到来的趋势一定只会进入更加发散的境地,因此这里,没有总结。 本篇文章为转载内容。原文链接:https://blog.csdn.net/systemino/article/details/90114743。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-05 13:33:43
300
转载
转载文章
...ct.config.json 文件中添加 "miniprogramRoot": "/", 2.在项目根目录创建 functions 文件夹 3.右键点击文件夹 选着 新建Node.js云函数 我的云函数名字 叫 checkStr(可以自定义) 点击之后会生成这三个文件 如果没有 请手动添加 config.json {"permissions": {"openapi": ["security.msgSecCheck"]} } index.js // 云函数入口文件 index.jsconst cloud = require('wx-server-sdk')cloud.init()// 云函数入口函数exports.main = async (event, context) => {const wxContext = cloud.getWXContext()try {const result = await cloud.openapi.security.msgSecCheck({content: event.text?event.text:'1'})if (result && result.errCode.toString() === '87014'){return { code: 500, msg: '内容含有违法违规内容', data: result } }else{return { code: 200, msg: 'ok', data: result } }} catch (err) {// 错误处理if (err.errCode.toString() === '87014') {return { code: 500, msg: '内容含有违法违规内容', data: err } }return { code: 502, msg: '调用security接口异常', data: err } }} package.json {"name": "checkStr","version": "1.0.0","description": "","main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"author": "","license": "ISC","dependencies": {"wx-server-sdk": "~2.3.1"} } 4.右键点击 云函数文件夹 checkStr 实例文件夹 点击上传并部署:云端安装依赖(不上传node_modules) 上传成功之后再右键点击当前文件夹 点击本地调试 会跳出来云函数界面 勾选本地调试 5.在页面中使用 wx.cloud.init();wx.cloud.callFunction({name: 'checkStr',data: {text: e.detail.value?e.detail.value:'1' // 这一步是处理输入框值手动清空的时候会被检测出敏感词,不知道什么原因抱歉} }).then((res) => {if (res.result.code == "200") {this.setData({sendValue: e.detail.value})} else {this.setData({sendValue: ''})wx.showToast({title: '包含敏感字哦。',icon: 'none',duration: 3000})} }) 本篇文章为转载内容。原文链接:https://blog.csdn.net/weixin_42046201/article/details/108998434。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-07-20 15:53:16
102
转载
JSON
JSON数据键的值,应该如何写多次换行的内容呢? 一、初识JSON 为什么我们要讨论这个? 大家好呀!今天我们来聊聊JSON——一种轻量级的数据交换格式。它就像是数据世界里的“万金油”,在前端和后端之间搭起了一座沟通的桥,让两边能顺畅地聊起天来。不过,今天我们要聊的可不是它的基本用法,而是稍微有点小复杂的问题:如何在JSON中表示包含多次换行的内容? 先别急着翻白眼,这其实是个很有意思的事情。想象一下,如果你要存储一段长篇小说或者多段落的文本信息,而这些内容又包含了换行符,那么该如何优雅地处理呢?是不是有点挠头?但别担心,作为一个热爱折腾的程序员,我决定带你一起探索这个问题! --- 二、JSON的基本规则 它不是魔法,但也不是障碍 首先,咱们得知道JSON的基本规则。JSON是一种基于文本的数据格式,主要由键值对组成。每个键必须是字符串,并且键和值之间需要用冒号分隔。至于值嘛,它可以是字符串、数字、布尔值、数组甚至是嵌套的对象。 比如这样: json { "name": "张三", "age": 25, "isStudent": false, "hobbies": ["reading", "coding"] } 看起来很简单吧?但是,当我们尝试存储一些更复杂的文本内容时,事情就没那么简单了。比如你想存一首诗,或者一封邮件,里面可能有好多换行符,那怎么办呢? --- 三、问题来了 换行符的“尴尬”存在 假设你正在写一个应用程序,需要让用户输入一段多行的文字,比如他们的个人简介。哎,你说如果用户输入的内容里带换行符怎么办?难道直接一股脑儿扔进JSON里?但问题来了啊,JSON这小家伙自己也不太争气,它压根儿就不允许字符串里直接留着换行符呢!这可咋整?除非你用某种方式告诉它,“嘿,这可是真的换行哦!” 这就像是你在写信的时候,突然发现信纸不够宽,只能把一句话分成两行写。而你的朋友收到信后,还得脑补那些断开的部分重新组合起来。所以,我们得想个办法让JSON能够正确地解析这些换行符。 --- 四、解决方案 转义字符登场! 幸运的是,JSON提供了一种非常聪明的方式来解决这个问题——转义字符。具体来说,如果你想在JSON字符串中表示换行符,可以使用\n来代替。这里的\n是一个特殊的符号,代表一个换行操作。 举个例子: json { "poem": "静夜思\n床前明月光,\n疑是地上霜。\n举头望明月,\n低头思故乡。" } 在这个例子中,我们用\n来表示每一句诗之间的换行。当你把这个JSON解析出来时,程序会自动把这些\n替换成实际的换行符,于是输出的结果就会变成: 静夜思 床前明月光, 疑是地上霜。 举头望明月, 低头思故乡。 是不是很神奇?不过,这里有一个小技巧需要注意:如果你想要表示真正的反斜杠(\),那么你需要用双反斜杠(\\)来表示。因为单个反斜杠在JSON中会被认为是一个转义符。 --- 五、更复杂的情况 多段落文本 当然,现实中的情况往往比一首诗复杂得多。比如说,你得把一封邮件的内容存下来,而这封邮件的正文往往是由好几段话组成的,有长有短,啥样的都有。哎呀,光靠换行符 \n 可不一定行啊,毕竟你还得让每段之间留点空白,不然读起来就像一锅粥,分不清哪是哪呀! 在这种情况下,你可以继续使用\n,同时注意合理安排段落结构。例如: json { "email": "亲爱的李四:\n\n很高兴收到您的来信。以下是我的回复:\n\n第一段内容...\n第二段内容..." } 在这里,\n\n表示两个连续的换行符,从而形成了一段空行。用这种方法,就能把文章分得清清楚楚的,读起来也顺溜多了! --- 六、代码实践 从理论到实战 说了这么多理论,让我们动手试试看吧!下面是一些简单的代码示例,展示如何在JavaScript中生成和解析带有换行符的JSON数据。 示例1:生成JSON字符串 javascript const data = { poem: "静夜思\n床前明月光,\n疑是地上霜。\n举头望明月,\n低头思故乡。", email: "亲爱的李四:\n\n很高兴收到您的来信。以下是我的回复:\n\n第一段内容...\n第二段内容..." }; // 将对象转换为JSON字符串 const jsonString = JSON.stringify(data); console.log(jsonString); 运行这段代码后,你会看到类似这样的输出: json {"poem":"静夜思\\n床前明月光,\\n疑是地上霜。\\n举头望明月,\\n低头思故乡。","email":"亲爱的李四:\\n\\n很高兴收到您的来信。以下是我的回复:\\n\\n第一段内容...\\n第二段内容..."} 可以看到,在生成的JSON字符串中,所有的\n都被转义成了\\n。 示例2:解析JSON字符串 javascript const jsonString = '{"poem":"静夜思\\n床前明月光,\\n疑是地上霜。\\n举头望明月,\\n低头思故乡。","email":"亲爱的李四:\\n\\n很高兴收到您的来信。以下是我的回复:\\n\\n第一段内容...\\n第二段内容..."}'; // 将JSON字符串解析回对象 const parsedData = JSON.parse(jsonString); console.log(parsedData.poem); console.log(parsedData.email); 运行这段代码后,你会看到如下输出: 静夜思 床前明月光, 疑是地上霜。 举头望明月, 低头思故乡。 亲爱的李四: 很高兴收到您的来信。以下是我的回复: 第一段内容... 第二段内容... 瞧!我们的换行符终于生效啦! --- 七、总结与反思 好了,今天的分享就到这里啦!通过这篇文章,我们不仅了解了如何在JSON中处理多次换行的内容,还学习了一些实用的小技巧。虽然JSON看似简单,但它背后隐藏着很多有趣的细节。希望这些知识能帮助你在未来的编程旅程中更加游刃有余。 最后,我想说的是,编程不仅仅是冷冰冰的技术活儿,它也是一种艺术形式。每一次解决问题的过程,都充满了挑战和乐趣。所以,不管遇到什么困难,都别轻易放弃,试着去思考、去尝试,说不定下一个突破就在前方等着你呢! 祝大家 coding愉快! 😊
2025-04-02 15:38:06
51
时光倒流_
转载文章
...nsole.log(JSON.stringify(res.tempFilePaths));} }); 2. app权限配置要勾选上对应的权限,如图: <uses-feature android:name=“android.hardware.camera” /> <uses-feature android:name=“android.hardware.camera.autofocus” /> <!-- 摄像头权限 --> <uses-permission android:name=“android.permission.CAMERA” /> <!-- 开启闪光灯权限 --> <uses-permission android:name=“android.permission.FLASHLIGHT”/> app模块配置勾选相机相册 以上三点都没问题了,打包之后的app应该就可以调用原生相机功能了。我出现无法调起来的原因是第三不,没有配置模块。 本篇文章为转载内容。原文链接:https://blog.csdn.net/jieyucx/article/details/130319786。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-08-01 22:36:09
32
转载
Docker
...b服务器响应慢或其他原因导致Docker客户端无法在设定时间内完成操作。 Daemon.json , Daemon.json是Docker守护进程的配置文件,用于设置Docker daemon启动时的各种参数和配置选项。在文章中,通过修改这个文件可以调整Docker的超时限制以及其他相关功能,例如并发下载和上传容器镜像的数量限制,以及设置Docker Hub的镜像仓库镜像源等。 iptables , iptables是一种Linux内核提供的数据包过滤表,可以对流入、流出和经过Linux主机的数据包进行控制,包括允许、丢弃、重定向等操作。在Docker环境下,iptables常被用于配置容器的网络规则,以保证容器间的网络隔离和通信。在本文中,将iptables设置为false可能是为了避免其对Docker网络通信造成潜在影响,进而解决超时问题。
2023-10-26 09:32:48
557
电脑达人
JSON
JSON对象里的数据取不到?一探究竟! 在我们的日常开发中,JSON(JavaScript Object Notation)作为轻量级的数据交换格式,广泛应用于前后端交互、配置文件读写等多种场景。然而,有时候我们会遇到一个让人头疼的常见问题:那个JSON对象明明近在眼前,可就是没法顺利拿到我们想要的具体数据。本文将通过实例探讨和解析这个问题,力求帮你拨开迷雾,掌握JSON数据的正确获取方式。 1. JSON基础与问题概述 首先,我们来回顾一下JSON的基本结构。你知道JSON吗?它其实是一种特别实用的数据存储格式,就像咱们平时用的小字典一样,里边的内容都是一对一对的放着。这里的“一对”就是键值对,键呢,相当于字典里的词条名称,人家规定必须得是字符串形式的;而值呢,就灵活多啦,可以是字符串、数字(整数、小数都行)、布尔值(也就是真或假),还能是数组(也就是一组数据打包在一起)、null(表示空或者无值)或者是另一个包含这些元素在内的JSON对象。是不是感觉挺丰富多彩的呀?例如: javascript let json = { "name": "John", "age": 30, "city": "New York", "hobbies": ["reading", "gaming"] }; 当我们在尝试从这样的JSON对象中提取数据时,如果出现了“取不到”的情况,可能是以下几个原因导致的: - 键名拼写错误或大小写不匹配。 - 路径引用错误,特别是在处理嵌套的JSON对象时。 - 数据类型判断错误,比如误以为某个值存在但实际上为undefined或null。 2. 键名错误引发的数据取不到 假设我们要从上述json对象中获取name属性,正确的做法如下: javascript console.log(json.name); // 输出: John 但如果我们将键名写错,如: javascript console.log(json.nmae); // 输出: undefined 此时就会出现“取不到”数据的情况,因为实际上并不存在名为nmae的属性。所以,在你捣鼓JSON的时候,千万要留意键名可得整准确了,而且记住啊,在JavaScript这个小淘气里,对象的属性名那可是大小写“斤斤计较”的。 3. 嵌套对象路径引用错误 对于嵌套的JSON对象,我们需要明确地指定完整路径才能访问到内部属性。例如: javascript let complexJson = { "user": { "name": "Alice", "address": { "city": "San Francisco" } } }; // 正确的方式: console.log(complexJson.user.address.city); // 输出: San Francisco // 错误的方式: console.log(complexJson.user.city); // 输出: undefined 这里可以看到,如果我们没有正确地按照路径逐层深入,同样会导致数据无法获取。 4. 数据类型的判断与处理 有时,JSON中的某个属性可能并未赋值,或者被设置为null。在访问这些属性时,需要做适当的检查: javascript let partialJson = { "name": null, "age": 35 }; // 直接访问未定义或null的属性 console.log(partialJson.name); // 输出: null // 在访问前进行条件判断 if (partialJson.name !== undefined && partialJson.name !== null) { console.log(partialJson.name); } else { console.log('Name is not defined or null'); } 5. 结论与思考 面对JSON对象中的数据取不到的问题,关键在于理解其底层逻辑和结构,并结合实际应用场景仔细排查。记住,每一次看似无法获取的数据背后,都有可能是细节上的小差错在作祟。只有细致入微,才能真正把握住这看似简单的JSON世界,让数据在手中自由流转。下次再碰到这种问题,咱们可以先别急着一头栽进去,不如先把节奏放缓,把思路缕一缕,一步步抽丝剥茧地分析看看。这样说不定就能火速找准问题的症结所在,然后轻轻松松就把问题给解决了。
2023-04-06 16:05:55
719
烟雨江南
Golang
...ecoder := json.NewDecoder(file) if err := decoder.Decode(&config); err != nil { return nil, fmt.Errorf("解析配置文件失败: %w", err) } return config, nil } func main() { config, err := readConfig("config.json") if err != nil { log.Fatalf("读取配置文件失败: %v", err) } // 使用配置... } 在这个例子中,readConfig函数尝试打开并解析一个JSON格式的配置文件。如果任何一步失败,我们都会返回一个包含原始错误的错误对象。这样做不仅可以让错误信息更加完整,还便于我们在调用方进行统一处理。 3.3 自定义错误类型 虽然标准库提供的error接口已经足够强大,但在某些场景下,我们可能需要更丰富的错误信息。这时,可以定义自己的错误类型来扩展功能。 go type MyError struct { Message string Code int } func (e MyError) Error() string { return fmt.Sprintf("错误代码%d: %s", e.Code, e.Message) } func doSomething() error { return &MyError{Message: "操作失败", Code: 500} } func main() { err := doSomething() if err != nil { log.Printf("发生错误: %v", err) } } 在这个例子中,我们定义了一个自定义错误类型MyError,它包含了一个消息和一个错误码。这样做的好处是可以根据不同的错误码采取不同的处理策略。 4. 错误信息的最佳实践 最后,我想分享一些我在日常开发中积累的经验,这些经验有助于写出更好的错误信息。 - 明确且具体:错误信息应该直接指出问题所在,避免模糊不清的描述。 - 用户友好的:对于最终用户可见的错误信息,尽量使用通俗易懂的语言。 - 提供解决方案:如果可能的话,给出一些基本的解决建议。 - 避免泄露敏感信息:在生成错误信息时,注意不要暴露敏感数据,如密码或密钥。 结语 错误信息是我们与程序之间的桥梁,它能帮助我们更好地理解问题所在,并找到解决问题的方法。在Go语言里,错误处理不仅仅是个技术活儿,它还代表着一种态度——就是要做出高质量的软件的那种执着精神。希望通过这篇文章,你能在未来的项目中更加重视错误信息的处理,从而写出更加健壮和可靠的代码。 --- 以上内容结合了理论与实践,旨在让你对Go语言中的错误处理有更深的理解。记住,好的错误信息就像是一位优秀的导游,它能带你穿越迷雾,找到正确的方向。
2024-11-09 16:13:46
127
桃李春风一杯酒
JSON
JSON , JavaScript Object Notation,是一种轻量级的数据交换格式。它基于纯文本,采用完全独立于语言的、清晰简洁的语法来表示键值对集合、数组和其他复杂数据结构,易于阅读和编写,并且能够被机器(特别是JavaScript引擎)直接解析和生成,广泛应用于Web服务接口、配置文件、数据存储等领域中,实现不同系统间的数据交换。 Python字典 , 在Python编程语言中,字典是一种可变容器模型,且可存储任意类型对象(如字符串、数字、元组等)的无序集合。每个项由一个唯一的键和与之关联的值组成,键和值之间通过冒号分隔,各对键值之间用逗号分隔,并放在花括号内。在处理JSON数据时,JSON对象通常会被转换为Python字典,以便在Python程序内部进行操作和处理。 JSON注入 , 类似于SQL注入,JSON注入是一种安全漏洞,攻击者通过向应用提交恶意构造的JSON数据,利用目标应用程序未能有效验证或清理用户输入的问题,进而影响服务器端JSON解析器的行为,以达到篡改数据、执行非预期操作或获取敏感信息的目的。在Python中使用json.loads()方法解析JSON字符串时,应结合严格的数据验证和清理措施来防止JSON注入攻击。
2024-03-03 16:01:36
529
码农
Docker
...镜像源 , 由于网络原因,直接从国外的Docker Hub获取镜像可能会遇到速度慢或无法连接的问题。国内镜像源是针对这一问题提供的一种解决方案,它通常是托管在国内服务器上的Docker Registry服务,用于同步或缓存Docker Hub上的官方镜像。用户通过配置Docker以使用这些国内镜像源,可以提高镜像拉取的速度和稳定性。 daemon.json , 在Docker中,daemon.json是一个重要的配置文件,用于存储Docker守护进程的全局配置选项。当需要更改Docker的默认行为,如添加镜像加速器(registry mirror)、设置日志级别等时,就需要编辑这个文件。在本文中,作者通过修改daemon.json文件中的\ registry-mirrors\ 字段,指定使用国内的镜像源。 systemctl , systemctl是Systemd系统和服务管理器的一部分,用于管理系统级别的服务,例如启动、停止、重启服务以及查看服务状态等。在本文中,作者使用systemctl命令来重新加载Docker服务的配置并重启Docker服务,以便新的镜像源配置生效。
2024-03-06 16:10:51
401
程序媛
Docker
...后,我发现这个问题的原因是拉取镜像环境中的一个配置项,即‘registry-mirrors’。 $ 拉取镜像 info ... Registry Mirrors: https://...:/ https://...:/ ... 我的问题是因为registry-mirrors设置了错误的映像库房地址,导致不能获取映像。在我的拉取镜像环境中,registry-mirrors配置文件存放的位置为/etc/拉取镜像/daemon.json。我打开这个文件,发现我的映像库房地址已经被设置为错误的地址。我修改这个地址后,重新运行拉取镜像 pull指令,成功地获取了需要的映像。 $ sudo vim /etc/拉取镜像/daemon.json { "registry-mirrors": ["https://registry.拉取镜像-cn.com"] } 总之,这个问题还是比较诡异的,因为我并没有修改什么拉取镜像的配置项,却产生了这样的问题。如果你也碰到了类似的问题,可以先检查一下映像库房地址是否正确,或者检查拉取镜像的一些其他配置项。
2023-04-18 10:38:27
371
算法侠
转载文章
...aths[0];} 原因,通过 console.log(JSON.stringify(res.tempFilePaths)); 可知 16:31:14.617 ["_doc/uniapp_temp_1587198502520/compressed/1587198670833.jpeg"] at pages\my\user-set-info.vue:81 res.tempFilePaths为数组,所以当上传一张图片时,取得图片临时地址为res.tempFilePaths[0]; 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_41884068/article/details/105601975。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-03-05 15:38:13
59
转载
Go Iris
...2. 数据提交失败的原因分析 在开始之前,我们先要了解数据提交失败可能的原因。通常,这类问题可以归结为以下几点: - 前端表单配置错误:比如表单字段名不匹配、缺少必要的字段等。 - 后端验证逻辑错误:如忘记添加验证规则、验证规则设置不当等。 - 编码问题:比如表单编码类型(Content-Type)设置错误。 接下来,我们将逐一排查这些问题,并给出相应的解决方案。 3. 前端表单配置错误 示例1:表单字段名不匹配 假设我们在前端表单中定义了一个名为username的输入框,但在后端接收时却命名为user_name。这种情况会导致数据提交失败。我们需要确保前后端字段名称一致。 html Submit go // 后端处理 import ( "github.com/kataras/iris/v12" ) func submit(ctx iris.Context) { var form struct { Username string validate:"required" } if err := ctx.ReadForm(&form); err != nil { ctx.StatusCode(iris.StatusBadRequest) ctx.JSON(map[string]string{"error": "Invalid form data"}) return } // 处理表单数据... } 在这个例子中,我们需要确保name="username"与结构体中的字段名一致。 示例2:缺少必要字段 如果表单缺少了必要的字段,同样会导致数据提交失败。例如,如果我们需要email字段,但表单中没有包含它。 html Submit go // 后端处理 import ( "github.com/kataras/iris/v12" ) func submit(ctx iris.Context) { var form struct { Username string validate:"required" Email string validate:"required,email" } if err := ctx.ReadForm(&form); err != nil { ctx.StatusCode(iris.StatusBadRequest) ctx.JSON(map[string]string{"error": "Missing required fields"}) return } // 处理表单数据... } 在这个例子中,我们需要确保所有必要字段都存在于表单中,并且在后端正确地进行了验证。 4. 后端验证逻辑错误 示例3:忘记添加验证规则 有时候,我们可能会忘记给某个字段添加验证规则,导致数据提交失败。比如说,我们忘了给password字段加上最小长度的限制。 html Submit go // 后端处理 import ( "github.com/kataras/iris/v12" "github.com/asaskevich/govalidator" ) func submit(ctx iris.Context) { var form struct { Username string valid:"required" Password string valid:"required" } if _, err := govalidator.ValidateStruct(form); err != nil { ctx.StatusCode(iris.StatusBadRequest) ctx.JSON(map[string]string{"error": "Validation failed: " + err.Error()}) return } // 处理表单数据... } 在这个例子中,我们需要确保所有字段都有适当的验证规则,并且在后端正确地进行了验证。 示例4:验证规则设置不当 验证规则设置不当也会导致数据提交失败。比如,我们本来把minlen设成了6,但其实得要8位以上的密码才安全。 html Submit go // 后端处理 import ( "github.com/kataras/iris/v12" "github.com/asaskevich/govalidator" ) func submit(ctx iris.Context) { var form struct { Username string valid:"required" Password string valid:"minlen=8" } if _, err := govalidator.ValidateStruct(form); err != nil { ctx.StatusCode(iris.StatusBadRequest) ctx.JSON(map[string]string{"error": "Validation failed: " + err.Error()}) return } // 处理表单数据... } 在这个例子中,我们需要确保验证规则设置得当,并且在后端正确地进行了验证。 5. 编码问题 示例5:Content-Type 设置错误 如果表单的Content-Type设置错误,也会导致数据提交失败。例如,如果我们使用application/json而不是application/x-www-form-urlencoded。 html Submit go // 后端处理 import ( "github.com/kataras/iris/v12" ) func submit(ctx iris.Context) { var form struct { Username string validate:"required" Password string validate:"required" } if err := ctx.ReadJSON(&form); err != nil { ctx.StatusCode(iris.StatusBadRequest) ctx.JSON(map[string]string{"error": "Invalid JSON data"}) return } // 处理表单数据... } 在这个例子中,我们需要确保Content-Type设置正确,并且在后端正确地读取了数据。 6. 结论 通过以上几个示例,我们可以看到,解决表单数据提交失败的问题需要从多个角度进行排查。不管是前端的表单设置、后端的验证规则还是代码里的小毛病,咱们都得仔仔细细地检查和调整才行。希望这些示例能帮助你更好地理解和解决这个问题。如果你还有其他问题或者发现新的解决方案,欢迎在评论区交流! 最后,我想说的是,编程之路充满了挑战和乐趣。每一次解决问题的过程都是成长的机会。希望这篇文章能给你带来一些启发和帮助!
2025-03-04 16:13:10
51
岁月静好
NodeJS
...么做,还能弄懂背后的原因。好了,废话不多说,让我们开始吧! 1. 为什么要生成API文档? 首先,我们需要知道为什么要在项目中生成API文档。设想一下,你正在捣鼓一个超级复杂的系统,这时候有几个团队陆陆续续地加入进来。如果连个像样的文档都没有,那他们可就得花不少功夫才能摸清你的API是个啥情况了。另外,API文档对测试小哥或者测试小姐姐来说也超重要,有了它,他们就能写出更靠谱的测试用例啦!所以,生成API文档不仅是为了自己方便,也是为了团队协作更加顺畅。 2. 选择合适的工具 接下来,我们要解决的问题是选择哪个工具来生成API文档。这里有几个非常流行的选择,比如Swagger、Postman、Docco等。今天咱们主要聊聊用Swagger来生成API文档,因为这个工具不仅特能干,而且还有个挺活跃的社区撑腰。Swagger可以让你定义一个API的结构,然后自动生成文档页面,甚至还可以提供一个交互式的API测试环境。 3. 安装Swagger 现在,让我们实际动手安装一下Swagger。打开你的终端,输入以下命令: bash npm install -g swagger-cli 这条命令会全局安装Swagger CLI工具,这样你就可以在任何地方直接运行Swagger命令了。当然,如果你不想全局安装,也可以在项目的本地安装Swagger,只需要在项目的根目录下运行: bash npm install --save-dev swagger-cli 4. 创建一个基本的API文档 安装完Swagger之后,我们就要开始创建我们的API文档了。来个简单点儿的例子吧,比如说咱们有个小破API,就用来捞用户的资料。首先,我们需要创建一个名为swagger.yaml的文件,并在其中定义我们的API。 yaml swagger: '2.0' info: version: "1.0.0" title: "User API" host: "localhost:3000" basePath: "/api" schemes: - "http" paths: /users/{userId}: get: description: "Get user by ID" parameters: - name: "userId" in: "path" description: "ID of user to fetch" required: true type: "integer" responses: 200: description: "successful operation" schema: $ref: "/definitions/User" definitions: User: type: "object" properties: id: type: "integer" username: type: "string" firstName: type: "string" lastName: type: "string" email: type: "string" password: type: "string" phone: type: "string" userStatus: type: "integer" description: "User Status" 这段代码定义了一个GET请求,用来根据用户ID获取用户信息。你可以看到,我们定义了一些参数和响应的内容。这只是一个非常基础的例子,实际上你可以定义更复杂的API。 5. 生成API文档 有了上面的定义文件之后,我们可以使用Swagger CLI工具来生成API文档。在终端中运行以下命令: bash swagger-cli validate swagger.yaml swagger-cli bundle swagger.yaml -o swagger.json swagger-cli serve swagger.json 这几条命令会验证你的定义文件是否正确,然后将它转换成JSON格式,并启动一个本地服务器来预览生成的API文档。打开浏览器,访问http://localhost:8080,你就能看到你的API文档啦! 6. 探索与扩展 生成API文档只是第一步,更重要的是如何维护和更新它。每当你的API发生变化时,记得及时更新文档。另外,你还可以试试用些自动化工具,在CI/CD流程里自动跑这些命令,这样每次部署完就能顺手生成最新的API文档了。 结语 好了,到这里我们就完成了使用Node.js生成API文档的基本教程。希望这篇文章能帮助你在实际工作中更好地管理和维护API文档。记住,良好的文档不仅能够提高开发效率,还能让团队协作更加高效。最后,如果有什么问题或者需要进一步的帮助,欢迎随时提问哦! --- 希望这篇文章对你有所帮助,如果你有任何疑问或者想要了解更多细节,不妨继续深入研究。加油!
2025-02-14 15:48:24
61
春暖花开
Go Gin
...连接闹了小脾气,这些原因都有可能导致这个问题出现。在这篇文章里,咱们打算手把手带你通过一个实际的场景案例,来摸清楚怎么用Go Gin框架巧妙地应对这种类型的异常情况,让你学得轻松又有趣。 二、案例分析 假设我们正在开发一个在线商店系统,用户可以在这个系统中注册账户并进行购物。在这个过程中,我们需要将用户的信息插入到数据库中。如果用户输入的数据有偏差,或者数据库连接闹起了小情绪,我们得赶紧把这些意外状况给捉住,然后给用户回个既友好又贴心的错误提示。 三、代码示例 首先,我们需要引入必要的包: go import ( "fmt" "github.com/gin-gonic/gin" ) 然后,我们可以定义一个路由来处理用户的注册请求: go func register(c gin.Context) { var user User if err := c.ShouldBindJSON(&user); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // 这里省略了数据库操作的具体代码 } 在这个函数中,我们首先使用ShouldBindJSON方法解析用户提交的JSON数据。这个方法会检查数据是否符合我们的结构体,并且可以自动处理一些常见的错误,比如字段不存在、字段类型不匹配等。 如果解析成功,那么我们就可以继续执行数据库操作。否则,我们就直接返回一个HTTP 400响应,告诉用户数据无效。 四、结论 通过以上的内容,我们已经了解了如何使用Go Gin框架来处理数据库插入异常。虽然这只是个小小例子,不过它可真能帮咱摸透异常处理那些最基本的道理和关键技术点。 在实际开发中,我们可能还需要处理更多复杂的异常情况,比如并发冲突、事务回滚等。为了更好地对付这些难题,我们得时刻保持学习新技能、掌握新工具的热情,而且啊,咱还得持续地给我们的代码“动手术”,让它更加精炼高效。只有这样,我们才能写出高质量、高效率的程序,为用户提供更好的服务。
2023-05-17 12:57:54
470
人生如戏-t
Mongo
...L数据库系统,它使用JSON-like格式(称为BSON)来存储数据,并提供了高可用性、水平扩展以及灵活的数据模型。在本文中,用户遇到的“Error Establishing Connection to Database”错误就是在尝试连接MongoDB数据库时可能出现的问题。 防火墙 , 防火墙是一种计算机网络安全技术,用于监控和控制进出特定网络或系统的网络流量。在本文语境下,防火墙可能阻止了MongoDB服务器接收来自其他设备的连接请求,导致数据库连接失败。通过配置防火墙规则,可以允许特定服务如MongoDB接受合法的外部连接,确保数据库能够正常对外提供服务。 连接池 , 虽然本文未直接提到连接池,但在处理数据库连接问题时这是一个常见的概念。连接池是一种软件架构设计模式,其目的是在一个应用程序与数据库之间管理并复用多个预设的数据库连接资源,以减少频繁创建和销毁连接带来的性能开销。在高并发场景下,如果没有合理设置和管理连接池,可能会因连接数达到上限而导致新的数据库连接请求无法成功建立,从而出现“Error Establishing Connection to Database”的错误提示。
2023-01-20 22:27:31
124
凌波微步-t
Beego
...mismatch的原因有很多,最常见的可能是我们的URL参数数量与方法参数数量不匹配。比如,我们可能会在控制器里头设置了一个需要两个输入参数的方法,不过在URL地址里边只塞了一个参数,这就搞出了个参数数量对不上的情况。 另一个常见的原因是参数类型不匹配。比如,我们在某个方法里定了规矩,要求传进来一个字符串类型的参数。可实际情况是,从URL里塞过来的却是个整型参数,这就像是你明明约了朋友吃火锅,人家却带了份炒饭来,类型对不上啊,闹出了参数类型不匹配的问题。 四、如何解决URLroutingparametermismatch 解决URLroutingparametermismatch的问题并不是一件困难的事情,只需要我们遵循以下几个步骤: 1. 首先,我们需要检查我们的URL是否与我们控制器中的方法参数匹配。假如我们发现参数个数对不上,那咱们就得动手调整一下URL,确保把所有必不可少的参数都塞进去哈。 2. 如果参数数量是正确的,但是参数类型不匹配,那么我们就需要修改我们的方法,使其能够接受任何类型的参数。 3. 在修改完URL和方法之后,我们还需要重新测试我们的应用,确保所有的功能都能正常工作。 五、实战演练 让我们通过一个具体的例子来看一下如何解决URLroutingparametermismatch的问题。想象一下,我们正在捣鼓一个超简洁的博客平台,用户们只需轻轻一点URL链接,就能一览无余地瞧见每篇博客的所有详细内容啦!我们的控制器代码如下: go func Show(c context.Context) { blogId := c.ParamsGetInt64(":id") blog, err := models.GetBlogById(blogId) if err != nil { c.JSON(500, gin.H{"error": "Failed to get blog"}) return } c.JSON(200, gin.H{"blog": blog}) } 在这个例子中,我们的方法接受一个参数(即博客ID),然后从数据库中获取相应的博客信息。然而,我们的URL却只有一个参数(即/blog/123),这意味着我们的参数数量不匹配。 要解决这个问题,我们可以直接在URL中添加一个额外的参数,使其与我们的方法参数匹配。我们的URL应该是这样的:/blog/:id。 另外,我们还需要注意的是,我们的数据库查询函数可能会返回一个错误。如果碰到这种情况,咱们就得给用户返回一个500状态码了,同时别忘了告诉他们具体出了什么差错。 六、总结 总的来说,解决URLroutingparametermismatch的问题并不难,只需要我们仔细检查我们的URL和方法,并根据需要进行修改即可。然而,这个过程可能会有些繁琐,因为它涉及到许多细节。不过,只要我们坚持下去,最终肯定能成功解决问题。记住啊,编程这玩意儿就像一场永不停歇的学习升级打怪之旅,只有亲自上手实战操练,才能真正把这项技能玩得溜起来,把它变成咱的拿手好戏。
2023-10-21 23:31:23
277
半夏微凉-t
SeaTunnel
...其中最常见的一种就是JSON格式的数据。JSON这东西,可以说是个超级实用的数据传输小能手。它设计得既简单又轻便,不仅咱们人类读起来、写起来轻松愉快,连机器也能毫不费力地理解和生成它。就像是数据世界里的“通用语言”,让信息交换变得轻轻松松、简简单单。然而,在日常处理大量JSON数据时,我们免不了会遇到些小插曲,比如那个让人头疼的JSON解析异常问题。 在本文中,我们将以SeaTunnel为例,深入探讨如何解决JSON解析异常的问题,并给出具体的实例代码。 二、什么是SeaTunnel SeaTunnel是一个开源的实时数据同步系统,它主要用于将数据从一个地方快速、准确地同步到另一个地方。SeaTunnel支持多种数据源和目标,包括但不限于MySQL、Oracle、HBase、HDFS等。它还配备了一整套超级好用的API工具箱,让开发者能够轻轻松松地进行数据同步操作,就像玩乐高积木一样便捷。 三、JSON解析异常的原因 JSON解析异常通常发生在数据源返回的JSON格式错误的情况下。比如,假如数据源给咱们返回的JSON字符串里头混进了不应该出现的非法字符,或者整个结构乱七八糟,跟JSON的标准格式对不上号,这时候SeaTunnel可就不乐意了,它会立马抛出一个JSON解析异常来表达它的不满和抗议。 四、解决JSON解析异常的方法 对于JSON解析异常的问题,我们可以采取以下几种方法来解决: 1. 检查并修正数据源返回的JSON数据 这是最直接也是最有效的方法。我们完全可以通过瞅瞅数据源头返回的结果,像侦探破案那样,揪出引发解析异常的那个“罪魁祸首”,然后对症下药,把它修正过来。 2. 使用JSON解析库 SeaTunnel本身已经内置了对JSON的支持,但是如果数据源返回的JSON格式非常复杂,我们可能需要使用更强大的JSON解析库来进行处理。 3. 优化SeaTunnel配置 通过调整SeaTunnel的配置参数,我们可以让其更加灵活地处理各种类型的JSON数据。 五、实战演示 下面,我们将通过一个实际的例子,展示如何使用SeaTunnel处理JSON解析异常的问题。 假设我们需要从一个外部服务器上获取一些JSON格式的数据,并将其同步到本地数据库中。但是,这个服务器上的JSON数据格式有点儿“另类”,它里面掺杂了一大堆不合规的字符呢! 首先,我们需要修改SeaTunnel的配置,使其能够容忍这种特殊的JSON格式。具体来说,我们可以在配置文件中添加以下代码: yaml processors: - name: json properties: tolerant: true 然后,我们可以创建一个新的任务,用于从服务器上获取JSON数据: json { "name": "example", "sources": [ { "type": "http", "properties": { "url": "https://example.com/data.json" } } ], "sinks": [ { "type": "mysql", "properties": { "host": "localhost", "port": 3306, "username": "root", "password": "", "database": "example", "table": "data" } } ] } 最后,我们只需要运行 SeaTunnel 的命令,就可以开始同步数据了: bash ./seata-tunnel.sh run example 六、结论 总的来说,解决SeaTunnel中的JSON解析异常问题并不是一件困难的事情。只要我们掌握了正确的处理方法,就能够有效地避免这种情况的发生。同时,我们也可以利用SeaTunnel的强大功能,来处理各种复杂的JSON数据。
2023-12-05 08:21:31
338
桃李春风一杯酒-t
Beego
...= nil { c.JSON(http.StatusNotFound, gin.H{"error": "User not found"}) return } c.JSON(http.StatusOK, user) } 5. 结语 总之,代码质量的管理是一个持续的过程,需要我们不断地学习和实践。用Beego框架能让我们更快搞定这个活儿,不过到最后还得靠我们自己动手干才行。希望大家都能写出既优雅又高效的代码! 好了,今天的分享就到这里,如果你有任何问题或建议,欢迎随时交流。希望这篇文章对你有所帮助,也期待我们在未来的项目中一起努力,共同提高代码质量!
2024-12-21 15:47:33
65
凌波微步
转载文章
...erverlist.json和serverlist.lua,路径如下,看看是不是有这两份文件。 D:\mud2.0\logincenter\logincenter_win\config\serverlist.json D:\mud2.0\logincenter\logincenter_win\application\controllers\serverlist.lua 这2分文件是否存在,如果存在,那么看第6条,答案就在最上面。 6、列表文件里面的IP、端口、格式是不是正确的(这个导致不开门的原因最多) 按照正常的流程,开门之后,就会出现黄色的列表信息,如下图,没有出现,那么可能serverlist.lua文件有问题,这其中包括了里面的列表格式,这个非常重要,你们在修改的时候,记得只修改里面的IP和游戏名字,端口默认8088即可。更不要添加标点符号等,多一个或少空格都会导致这份文件无法加载,从而出现了不开门的情况,如果开门了,到这里点击进不去,也是因为你修改修改的时候,破坏了标准的Lua格式。 本篇文章为转载内容。原文链接:https://blog.csdn.net/qq_43410101/article/details/108263880。 该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。 作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。 如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。
2023-02-27 13:11:20
375
转载
Datax
...首先,你需要创建一个JSON配置文件。在这份文件里,你要指明数据从哪儿来、要去哪儿,还得填一些关键设置,比如说线程数量。 json { "job": { "content": [ { "reader": { "name": "mysqlreader", "parameter": { "username": "root", "password": "123456", "connection": [ { "jdbcUrl": ["jdbc:mysql://localhost:3306/testdb"], "table": ["user_info"] } ] } }, "writer": { "name": "hdfswriter", "parameter": { "defaultFS": "hdfs://localhost:9000", "fileType": "text", "path": "/user/datax/user_info", "fileName": "user_info.txt", "writeMode": "append", "column": [ "id", "name", "email" ], "fieldDelimiter": "\t" } } } ], "setting": { "speed": { "channel": 4 } } } } 在这段配置中,"channel": 4 这一行非常重要。它指定了DataX应该使用多少个线程来处理数据。这里的数字可以根据你的实际情况调整。比如说,如果你的电脑配置比较高,内存和CPU都很给力,那就可以试试设大一点的数值,比如8或者16。 5. 实战演练 为了更好地理解DataX的多线程处理,我们来看一个具体的实战案例。假设你有一个名为 user_info 的表,其中包含用户的ID、姓名和邮箱信息。现在你想把这部分数据同步到HDFS中。 首先,你需要确保已经安装并配置好了DataX。接着,按照上面的步骤创建一个JSON配置文件。这里是一些关键点: - 数据库连接:确保你提供的数据库连接信息(用户名、密码、JDBC URL)都是正确的。 - 表名:指定你要同步的表名。 - 字段列表:列出你要同步的字段。 - 线程数:根据你的需求设置合适的线程数。 保存好配置文件后,就可以运行DataX了。打开命令行,输入以下命令: bash python datax.py /path/to/your/config.json 注意替换 /path/to/your/config.json 为你的实际配置文件路径。运行后,DataX会自动启动指定数量的线程来处理数据同步任务。 6. 总结与展望 通过本文的介绍,你应该对如何使用DataX实现数据同步的多线程处理有了初步了解。多线程不仅能加快数据同步的速度,还能让你在处理海量数据时更加得心应手,感觉轻松不少。当然啦,这仅仅是DataX功能的冰山一角,它还有超多酷炫的功能等你来探索呢! 希望这篇文章对你有所帮助!如果你有任何问题或建议,欢迎随时留言交流。我们一起探索更多有趣的技术吧!
2025-02-09 15:55:03
76
断桥残雪
站内搜索
用于搜索本网站内部文章,支持栏目切换。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
fg %jobnumber
- 将后台作业切换至前台运行。
推荐内容
推荐本栏目内的其它文章,看看还有哪些文章让你感兴趣。
2023-04-28
2023-08-09
2023-06-18
2023-04-14
2023-02-18
2023-04-17
2024-01-11
2023-10-03
2023-09-09
2023-06-13
2023-08-07
2023-03-11
历史内容
快速导航到对应月份的历史文章列表。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"