×
关于Socket 编程发展OpenResty 简介Lua 入门Lua 简介Lua 环境搭建基础数据类型表达式控制结构if/elsewhilerepeatforbreak,returnLua函数函数的定义函数的参数函数返回值全动态函数调用模块String 库Table 库日期时间函数数学库函数文件操作

Lua 高阶

元表面向对象编程局部变量判断数组大小非空判断正则表达式不用标准库虚变量抵制使用 module() 定义模块调用代码前先定义函数点号与冒号操作符的区别module 是邪恶的FFI什么是 JITNginxNginx 新手起步location 匹配规则if 是邪恶的静态文件服务日志反向代理负载均衡陷阱和常见错误

OpenResty

环境搭建Windows 平台CentOS 平台Ubuntu 平台Mac OS X 平台Hello World与其他 location 配合获取 uri 参数获取请求 body输出响应体日志输出简单API Server框架使用 Nginx 内置绑定变量子查询不同阶段共享变量防止 SQL 注入如何发起新 HTTP 请求

LuaRestyRedisLibrary

访问有授权验证的 Redisselect+set_keepalive 组合操作引起的数据读写错误redis 接口的二次封装(简化建连、拆连等细节)redis 接口的二次封装(发布订阅)pipeline 压缩请求数量script 压缩复杂请求动态生成的 lua-resty-redis 模块方法LuaCjsonLibraryjson解析的异常捕获稀疏数组空table编码为array还是objectPostgresNginxModule调用方式简介不支持事务超时健康监测SQL注入LuaNginxModule执行阶段概念正确的记录日志热装载代码阻塞操作缓存sleep定时任务禁止某些终端访问请求返回后继续执行调试请求中断后的处理我的 lua 代码需要调优么变量的共享范围动态限速shared.dict 非队列性质正确使用长链接如何引用第三方 resty 库body 在 location 中的传递典型应用场景怎样理解 cosocket如何安全启动唯一实例的 timer如何正确的解析域名使用动态 DNS 来完成 HTTP 请求LuaRestyLock缓存失效风暴

stream_lua_module

balancer_by_lua

OpenResty 与 SSL

HTTPS 时代动态加载证书和 OCSP staplingTLS session resumption测试代码静态分析单元测试代码覆盖率API 测试性能测试持续集成灰度发布Web 服务API的设计数据合法性检测协议无痛升级代码规范连接池C10K 编程TIME_WAIT 问题与 Docker 使用的网络瓶颈火焰图什么时候使用如何定位问题

OpenResty 周边

如何添加自己的lua api

零碎知识点记录

2016-7 月汇总如何在后台开启轻量级线程来定时更新共享内存一个 openresty 内存“泄漏”问题用 do-end 整理你的代码lua 中如何 continue调用 FFI 出现 "table overflow"如何定位 openresty 崩溃 bug

SQL 注入


有使用 SQL 语句操作数据库的经验朋友,应该都知道使用 SQL 过程中有一个安全问题叫 SQL 注入。所谓 SQL 注入,就是通过把 SQL 命令插入到 Web 表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的 SQL 命令。 为了防止 SQL 注入,在生产环境中使用 OpenResty 的时候就要注意添加防范代码。

延续之前的 ngx_postgres 调用代码的使用,

    local sql_normal = [[select id, name from user where name=']] .. ngx.var.arg_name .. [[' and password=']] .. ngx.var.arg_password .. [[' limit 1;]]

    local res = ngx.location.capture('/postgres',
        { args = {sql = sql } }
    )

    local body = json.decode(res.body)

    if (table.getn(res) > 0) {
        return res[1];
    }

    return nil;

假设我们在用户登录使用上 SQL 语句查询账号是否账号密码正确,用户可以通过 GET 方式请求并发送登录信息比如:

#    http://localhost/login?name=person&password=12345

那么我们上面的代码通过 ngx.var.arg_name 和 ngx.var.arg_password 获取查询参数,并且与 SQL 语句格式进行字符串拼接,最终 sql_normal 会是这个样子的:

    local sql_normal = [[select id, name from user where name='person' and password='12345' limit 1;]]

正常情况下,如果 person 账号存在并且 password 是 12345,那么 sql 执行结果就应该是能返回 id 号的。这个接口如果暴露在攻击者面前,那么攻击者很可能会让参数这样传入:

    name="' or ''='"
    password="' or ''='"

那么这个 sql_normal 就会变成一个永远都能执行成功的语句了。

    local sql_normal = [[select id, name from user where name='' or ''='' and password='' or ''='' limit 1;]]

这就是一个简单的 sql inject(注入)的案例,那么问题来了,面对这么凶猛的攻击者,我们有什么办法防止这种 SQL 注入呢?

很简单,我们只要 把 传入参数的变量 做一次字符转义,把不该作为破坏 SQL 查询语句结构的双引号或者单引号等做转义,把 ' 转义成 ',那么变量 name 和 password 的内容还是乖乖的作为查询条件传入,他们再也不能为非作歹了。

那么怎么做到字符转义呢?要知道每个数据库支持的 SQL 语句格式都不太一样啊,尤其是双引号和单引号的应用上。有几个选择:

    ndk.set_var.set_quote_sql_str()
    ndk.set_var.set_quote_pgsql_str()
    ngx.quote_sql_str()

这三个函数,前面两个是 ndk.set_var 跳转调用,其实是 HttpSetMiscModule 这个模块提供的函数,是一个 C 模块实现的函数,ndk.set_var.set_quote_sql_str() 是用于 MySQL 格式的 SQL 语句字符转义,而 set_quote_pgsql_str 是用于 PostgreSQL 格式的 SQL 语句字符转义。最后 ngx.quote_sql_str 是一个 ngx_lua 模块中实现的函数,也是用于 MySQL 格式的 SQL 语句字符转义。

让我们看看代码怎么写:

    local name = ngx.quote_sql_str(ngx.var.arg_name)
    local password = ngx.quote_sql_str(ngx.var.arg_password)
    local sql_normal = [[select id, name from user where name=]] .. name .. [[ and password=]] .. password .. [[ limit 1;]]

    local res = ngx.location.capture('/postgres',
        { args = {sql = sql } }
    )

    local body = json.decode(res.body)

    if (table.getn(res) > 0) {
        return res[1];
    }

    return nil;

注意上述代码有两个变化:

  • 用 ngx.quote_sql_str 把 ngx.var.arg_name 和 ngx.var.arg_password 包了一层,把返回值作为 sql 语句拼凑起来。
  • 原本在 sql 语句中添加的单引号去掉了,因为 ngx.quote_sql_str 的返回值正确的带上引号了。

这样已经可以抵御 SQL 注入的攻击手段了,但开发过程中需要不断的产生新功能新代码,这时候也一定注意不要忽视 SQL 注入的防护,安全防御代码就想织网一样,只要有一处漏洞,鱼儿可就游走了。


分类导航

关注微信下载离线手册

bootwiki移动版 bootwiki
(群号:472910771)