Lua 表的一个安全访问问题

zip = company
	and company.director
	and company.director.address
	and company.director.address.zipcode

以上写法不仅冗长而且低效,该写法在一次成功的访问中对表进行了6次访问。

对于这种问题,c#语音提供了一种安全访问操作符"?."。例如,对于表达式a?.b,当a为null时,其结果是null而不会产生异常。使用该操作符,改写以上代码:

zip = company?.director?.address?.zipcode

Lua语言并没有提供安全访问操作符,并且认为也不应该提供她。不过我们可以使用其他语句在Lua语言中模拟安全访问操作符。

zip = (((company or {}).director or {}).address or {}).zipcode

进一步优化:

empty = {}

zip = (((company or empty).director or empty).address or empty).zipcode

以上写法,表中的每一个字段名都只被访问了一次。

 

本文作为阅读《Lua程序设计》的一个学习笔记内容。

OpenStar是一个基于OpenResty的,高性能web平台,不仅仅包含了传统WAF的功能模块,还相应增加了其他灵活、友好、实用的功能,是增强的WAF、WEB扩展、CC防护的集合。 WAF防护 在OpenStar中的WAF防护模块,采用传统的黑白名单、正则过滤的方式(有人会问现在不是流行自主学习么;正则、黑白名单会有盲点、会被绕过......)。这里我简单说明一下,自主分析学习引擎是我们的日志分析引擎做的,这里是高性能、高并发的点,就用简单粗暴的方法解决,根据业务实际调整好防护策略,可以解决绝大多数WEB安全1.0和WEB安全2.0类型的漏洞(90% 的问题)。 WAF 防护从header,args,post,访问频率等层面分层进行防护,详细在后面的功能会详细说明 WEB安全1.0 在1.0时代下,攻击是通过服务器漏洞(IIS6溢出等)、WEB应用漏洞(SQL注入、文件上传、命令执行、文件包含等)属于服务器类的攻击,该类型漏洞虽然经历了这么多年,很遗憾,此类漏洞还是存在,并且重复在犯相同的错误。 WEB安全2.0 随着社交网络的兴起,原来不被重视的XSS、CSRF等漏洞逐渐进入人们的视野,那么在2.0时代,漏洞利用的思想将更重要,发挥你的想象,可以有太多可能。 WEB安全3.0 同开发设计模式类似(界面、业务逻辑、数据),3.0将关注应用本身的业务逻辑和数据安全,如密码修改绕过、二级密码绕过、支付类漏洞、刷钱等类型的漏洞,故注重的是本身产品的业务安全、数据安全。 安全不仅仅是在技术层面、还应该在行政管理层面、物理层面去做好安全的防护,才能提供最大限度的保护。 安全行业多年的从业经验:人,才是最大的威胁;无论是外部、内部、无心、有意过失。(没有丑女人、只有懒女人)我想可以套用在此处,纯属个人见解。 CC/采集防护 什么是CC攻击,简单的说一下,就是用较少的代价恶意请求web(应用)中的重资源消耗点(CPU/IO/数据库等等)从而达到拒绝服务的目的;数据采集,就是内容抓取了,简单这么理解吧  非官方学术类的解释,先将就理解下 关于本文对CC攻击的分析和相关的防护算法,都是我在实战中分析总结,并形成自己的方法论,不足之处、欢迎指正。 标签:OpenStar  Web框架
<think> 我们正在讨论的是在C++层编写返回两个一个字符串的Lua脚本的实现方法。根据引用[2]和[3]的内容,我们可以通过Lua C API来实现。主要步骤包括: 1. 在C++中创建Lua状态机,加载并执行Lua脚本。 2. 从C++调用Lua函数,并获取其返回值。 3. Lua函数返回两个一个字符串,我们需要在C++中解析这些返回值。 具体步骤如下: ### 1. 编写Lua脚本 假设我们有一个Lua函数,它返回两个一个字符串。例如,在`example.lua`中: ```lua function myFunction() local table1 = {name="Alice", age=25} local table2 = {x=10, y=20, z=30} local str = "Hello from Lua" return table1, table2, str end ``` ### 2. C++调用Lua函数并处理返回值 在C++中,我们将: - 初始化Lua状态机。 - 加载并运行Lua脚本。 - 获取Lua函数并调用它。 - 从栈中取出返回的两个一个字符串。 #### 完整C++代码示例: ```cpp #include <iostream> #include <lua.hpp> int main() { // 创建Lua状态 lua_State* L = luaL_newstate(); luaL_openlibs(L); // 加载并运行Lua脚本 if (luaL_dofile(L, "example.lua") != LUA_OK) { std::cerr << "加载脚本错误: " << lua_tostring(L, -1) << std::endl; lua_close(L); return 1; } // 获取全局函数myFunction lua_getglobal(L, "myFunction"); if (lua_isfunction(L, -1)) { // 调用函数,无参数,返回3个值(两个一个字符串) if (lua_pcall(L, 0, 3, 0) != LUA_OK) { std::cerr << "调用函数错误: " << lua_tostring(L, -1) << std::endl; lua_close(L); return 1; } // 现在栈顶有三个返回值:字符串、2、1(注意:返回值的顺序是反向的,最后一个返回值在栈顶) // 因此,栈索引:-1是字符串,-2是2,-3是1?不对,实际上返回值按顺序入栈,第一个返回值最先入栈,所以在栈底。 // 具体:调用后栈顶是第一个返回值(1),然后上面是2,最后是字符串(第三个返回值在栈顶)。 // 所以索引:-1: 字符串, -2: 2, -3: 1 // 1. 处理第三个返回值(字符串,位于栈顶) if (lua_isstring(L, -1)) { const char* str = lua_tostring(L, -1); std::cout << "字符串: " << str << std::endl; } lua_pop(L, 1); // 弹出字符串 // 2. 处理第二个返回值(2,现在栈顶) if (lua_istable(L, -1)) { std::cout << "2: " << std::endl; // 遍历2 lua_pushnil(L); // 压入nil作为第一个键 while (lua_next(L, -2) != 0) { // 现在键在索引-2,值在索引-1 if (lua_isstring(L, -2)) { const char* key = lua_tostring(L, -2); if (lua_isnumber(L, -1)) { double value = lua_tonumber(L, -1); std::cout << " " << key << " = " << value << std::endl; } else if (lua_isstring(L, -1)) { const char* value = lua_tostring(L, -1); std::cout << " " << key << " = " << value << std::endl; } } // 弹出值,保留键用于下一次迭代 lua_pop(L, 1); } } lua_pop(L, 1); // 弹出2 // 3. 处理第一个返回值(1,现在栈顶) if (lua_istable(L, -1)) { std::cout << "1: " << std::endl; // 获取特定字段 lua_getfield(L, -1, "name"); if (lua_isstring(L, -1)) { const char* name = lua_tostring(L, -1); std::cout << " name: " << name << std::endl; } lua_pop(L, 1); // 弹出name lua_getfield(L, -1, "age"); if (lua_isnumber(L, -1)) { int age = lua_tointeger(L, -1); std::cout << " age: " << age << std::endl; } lua_pop(L, 1); // 弹出age } lua_pop(L, 1); // 弹出1 } else { std::cerr << "找不到函数 myFunction" << std::endl; } lua_close(L); return 0; } ``` ### 关键点说明: 1. **返回值顺序**:Lua函数调用后,返回值按顺序压入栈中,第一个返回值在栈底(相对索引从1开始),最后一个返回值在栈顶(索引-1)。 2. **遍历**:使用`lua_next`遍历时,需要先压入一个nil,然后循环调用`lua_next`。每次调用后,键值对会被压入栈,处理完后需要弹出值,保留键用于下一次迭代。 3. **获取字段**:使用`lua_getfield`可以直接获取中指定键的值,这会将该值压入栈顶。 4. **类型检查**:在取值前,务必使用`lua_is...`系列函数检查类型,避免错误。 5. **栈平衡**:注意维护栈的平衡,每次弹出不需要的值,避免栈溢出。 ### 注意事项 - 如果嵌套很深,需要递归处理,但本例中结构简单,直接处理即可。 - 错误处理非常重要,每个Lua API调用后都应检查错误。 - 在多线程环境下使用Lua状态机需要额外注意,因为Lua状态机不是线程安全的。 ### 参考文献 [^1]: 虚幻引擎编程中的LUA脚本访问C++原理与实现 [^2]: C++ 调用lua 脚本 [^3]: Lua教程(五):C/C++操作Lua数组和字符串示例 §§相关问题§§ 1. 如何在Lua中存储和返回嵌套结构? 2. C++调用Lua函数时如何传递复杂的参数(如)? 3. Lua和C++之间传递大量数据时如何优化性能? 4. 如何处理Lua函数返回的多个不同类型的值? </think> ### C++层编写返回两个一个字符串的Lua脚本实现方法 在C++层操作Lua脚本返回两个一个字符串,主要通过Lua C API实现。以下是分步实现方案: --- #### 1. **Lua脚本设计** 创建Lua函数返回所需数据结构(保存为`multi_return.lua`): ```lua function getData() local table1 = { engine="Unreal", version=5.3 } local table2 = { x=10, y=20, z=30 } local message = "Data loaded successfully" return table1, table2, message -- 返回两个一个字符串 end ``` --- #### 2. **C++调用与返回值处理** ```cpp #include <iostream> #include <lua.hpp> int main() { // 初始化Lua状态机 lua_State* L = luaL_newstate(); luaL_openlibs(L); // 加载Lua脚本 if (luaL_dofile(L, "multi_return.lua") != LUA_OK) { std::cerr << "脚本加载错误: " << lua_tostring(L, -1); lua_close(L); return 1; } // 获取Lua函数 lua_getglobal(L, "getData"); if (lua_isfunction(L, -1)) { // 调用函数(无参数,返回3个值) if (lua_pcall(L, 0, 3, 0) != LUA_OK) { std::cerr << "函数调用错误: " << lua_tostring(L, -1); lua_close(L); return 1; } // 栈结构(从底到顶): // 索引1: table1 // 索引2: table2 // 索引3: message(栈顶) // 1. 处理字符串(栈顶) if (lua_isstring(L, 3)) { const char* msg = lua_tostring(L, 3); std::cout << "消息: " << msg << std::endl; } // 2. 处理第二个(索引2) if (lua_istable(L, 2)) { std::cout << "\n2内容: "; lua_pushnil(L); // 遍历前的初始key while (lua_next(L, 2) != 0) { const char* key = lua_tostring(L, -2); double value = lua_tonumber(L, -1); std::cout << key << "=" << value << " "; lua_pop(L, 1); // 移除value,保留key } } // 3. 处理第一个(索引1) if (lua_istable(L, 1)) { // 直接获取字段值 lua_getfield(L, 1, "engine"); const char* engine = lua_tostring(L, -1); lua_pop(L, 1); lua_getfield(L, 1, "version"); double version = lua_tonumber(L, -1); lua_pop(L, 1); std::cout << "\n1内容: engine=" << engine << ", version=" << version; } // 清理栈(弹出3个返回值) lua_pop(L, 3); } else { std::cerr << "函数未找到"; } lua_close(L); return 0; } ``` --- ### 关键实现技术说明 1. **返回值顺序控制** Lua函数返回值的入栈顺序是固定的: $$ \text{[栈底]} \leftarrow \text{table1} \leftarrow \text{table2} \leftarrow \text{字符串} \leftarrow \text{[栈顶]} $$ 通过索引位置(1,2,3)精准定位[^2]。 2. **遍历技术** 使用`lua_next`遍历: ```cpp lua_pushnil(L); // 初始化迭代 while (lua_next(L, table_index) != 0) { // 栈顶: value // 栈顶-1: key // 处理数据... lua_pop(L, 1); // 弹出value保留key } ``` 3. **类型安全操作** - `lua_isstring()`/`lua_istable()` 检查类型 - `lua_toXXX()` 系列函数安全转换 - 操作后通过`lua_pop()`维护栈平衡 4. **内存管理** - `luaL_newstate()` 创建状态机 - `lua_close()` 释放资源 - 避免栈溢出(典型上限:$10^6$ 元素) --- ### 输出结果示例 ``` 消息: Data loaded successfully 2内容: z=30 y=20 x=10 1内容: engine=Unreal, version=5.3 ``` --- ### 注意事项 1. **栈索引规则** 正数索引:从栈底开始(1为第一个元素) 负数索引:从栈顶开始(-1为栈顶元素) 2. **错误处理** 所有Lua API调用后应检查: ```cpp if (lua_pcall(...) != LUA_OK) { // 处理错误 } ``` 3. **嵌套处理** 深层嵌套需使用递归遍历,参考Lua操作规范[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值