注入攻击在 OWASP Web 应用 10 大安全风险[1] 排名 2021 年下滑至第 3 位,多年来一直位居前十。SQL 注入 (SQLi) 是一种用于攻击网站和 Web 应用程序的常见注入技术。没有将用户输入与数据库命令完全分开的应用程序面临着将恶意输入作为 SQL 命令执行的风险。成功的注入攻击可能导致未经授权访问敏感数据,例如密码、信用卡详细信息和个人用户信息。SQL 注入攻击导致最近许多备受瞩目的数据泄露事件,导致企业声誉受损和监管罚款。
常见的解决方法是使用 正则表达式 列表来过滤流量,这在某些情况下有效,但对于一些复杂的输入或转义输入来说不足。我们并不是要抨击正则表达式,它们很好并且有自己的使用场景,它们只是不适合这种场景。
这篇文章将演示如何使用 Pipy[2] 一种开源可编程代理来加强安全性,方法是为应用程序添加额外的防护层,来避免受到此类攻击。
在上一篇宣布 Pipy 0.70.0 最新版本的博文中,介绍了 Pipy 添加了对名为 Native Module Interface (NMI)[3] 的扩展的支持,我们将使用 Pipy NMI 开发一个模块来集成成熟稳定的开源库 libinject[4],用于在请求流量到达应用程序之前针对 SQLi 和 XSS 攻击进行扫描。
作为演示,我们将使用一个开源的经典 LAMP 示例 Vulnerable Mama Shop (VMS)[5],该示例是故意具有 SQLi 缺陷。我们首先将通过破解基本应用程序来演示 SQLi 攻击,然后再通过添加 Pipy 作为 Sidecar 来阻止某些 SQLi 攻击来加强应用程序的安全性。
这篇文章假设有如下访问权限:
• 运行 Kubernetes 集群
• kubectl[6]
演示源代码在 GitHub 上,可以从 pipy-sqli-demo[7] 仓库下载。
要在本地运行演示,我们推荐 k3d[8] 一个轻量级包装器来在 docker 中运行 k3s[9](Rancher Lab 的最小 Kubernetes 发行版)。
$ k3d cluster create my-cluster -p 8080:30060@server:0
在上面的命令中,我们创建了一个单节点的集群,并将 k3d 容器的 30060 端口映射到本地 8080 端口,这个端口将在本教程的后面步骤中使用。
我们将部署一个简单的在线商店应用程序,该应用程序附带安装
• Apache 网络服务器
• MariaDB
• 在 Apache 上运行并连接到 MariaDB 数据库的 PHP 应用
1. 创建一个名为 1-app.yaml
的 YAML 文件,其中包含以下内容:
apiVersion: apps/v1
kind: Deployment
metadata:
name: vms
spec:
selector:
matchLabels:
app: vms
template:
metadata:
labels:
app: vms
spec:
containers:
- name: vms
image: naqvis/mamashop
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: vms
spec:
ports:
- port: 80
targetPort: 80
nodePort: 30060
selector:
app: vms
type: NodePort
1. 部署应用
$ kubectl apply -f 1-app.yaml
1. 确认 pod 已启动并运行,状态为 Running
。可能需要 30-40 秒才能完成启动,因此在继续下一步之前有必要再次运行命令以确认所有 pod 都正常运行。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
vms-6658dd4478-hn246 1/1 Running 0 2m12s
1. 查询服务
$ kubectl get svc vms
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
vms NodePort 10.43.244.253 80:30060/TCP 5m43s
还记得一开始我们为 K3d 容器做端口映射:8080=>30060 吗?细心的用户可能还会发现,我们为 Vulnerable Mama Shop Web 应用创建了一个 NodePort 服务,节点端口为 30060。
在浏览器中访问 http://localhost:8080 打开应用程序。
Mama Shop 应用非常简单,只有 3 个页面。主页(如上所示)是用户可以通过下拉框查询待售商品的页面、客户登录页面和关于页面。访问 3 个页面,看看每个页面的作用以及每个页面如何正常工作。
尝试提交一个类别,看看应用是如何展示的。下面显示了饮料类别中的商品列表。
可能已经注意到,选择类别并点击提交不会更改 URL,因此应用正在执行 POST 请求,我们将需要一些工具来拦截或查看网页产生的请求流量。
通常,使用诸如 Blurp 套件或 OWASP ZAP 之类的代理工具来拦截和修改对应用程序的请求。但为了简单起见,我们将使用 Firefox 浏览器开发者控制台来查看和修改请求。
返回浏览器(本演示中的 Firefox)并通过菜单选项或通过 Mac 上的快捷键 Option+Cmd+I
或操作系统上的类似功能打开 Web 开发人员工具。移动到 Tools | Browser Tools | Web Developer Tools
网络选项卡,然后点击网页上的提交按钮以查看网络流量。
我们可以看到所有请求以及详细信息,一个网页向 Web 服务器发送以显示该页面。
网络操作窗口显示,要浏览类别中的项目,一个带有 URL 编码的 catid
参数的 HTTP POST 请求被发送到 welcome.php
。要测试 SQL 注入,通常会修改用户输入并发送单引号,例如 '
。
我们将通过一个简单的实验来确定输入是否正确转义:将 catid 更改为 SQL 查询字符串,看看是否会导致网页上显示的语法错误。
catid=’ or 1 = 1 ; —
执行上述更改并点击网络操作窗口右下角的发送按钮。
哇,我们做到了,welcome.php
显示类别项目的功能存在 SQL 注入漏洞。错误消息还告诉我们数据库是 MariaDB。错误告诉还我们 or 1 = 1
附近存在语法问题。进一步,我们可以假设数据库查询类似于
SELECT item_name, item_category, item_description from items_table where item_category = ‘ or 1 = 1 ; — ;
如果我们将 catid
结尾更改为 “ or 1 = 1 — ;
,那应该可以解决引号问题。它应该从数据库中选择所有行,这在破解中很有用。
假定的 SQL 查询的结构可能是正确的,尽管它不一定是开发人员使用的精确查询。这对于窃取客户信息等破坏性攻击来说已经足够了。
客户登录页面告诉我们该应用包含客户数据,这可能存储在某种客户或用户表中。
根据目前收集的信息,可以使用 MariaDB INFORMATION_SCHEMA
和 SQL Union 运算符 来检索数据库和表的详细信息。
将参数更改 catid
为以下以检索数据库名称,该名称将进一步用于检索表详细信息。
catid=1000 union select database(), “A” , “B” from dual
下面显示了来自 Vulnerable Mama Shop 的包含数据库名称的响应 appdb
。
现在我们有了数据库名称,我们可以获取数据库中的表
catid=1000 union select table_name,version, table_comment from information_schema.TABLES where table_schema = ‘appdb’
这样就得到了所有表名,我们可以假设数据库中的 users 表包含用户名和密码。
我们需要检索 users
表的列,并且需要确保使用与从产品表中查询的相同数量的列 UNION
才能工作。
catid=1000 union select table_name, COLUMN_NAME, DATA_TYPE from information_schema.COLUMNS where table_name = ‘users’
现在我们知道 users
表中总共有六列,其中包含可以从 users
表中获得的 firstname
、lastname
、nric
、password
、email 等详细信息
。
我们已经收集了足够用来转储用户列表的信息。以下输入可用于转储用户列表及其 firstname
、password
和 email
地址。
catid=1000 union select firstname, nric, email from users LIMIT 7, 100
LIMIT
和 offset
的值可以通过观察正常请求中有多少条目来计算。offset
值可以删除这些条目。可以设置一个足够大的 LIMIT
值,以便可以转储客户记录。
可以尝试是否可以使用提取到的凭证信息来登录。
当未经验证的用户输入与 SQL 指令混合时会发生 SQL 注入,因此开发人员应更加注意转义用户输入(例如使用参数化查询),但作为 Kubernetes 工程师也可以通过防止这种情况来帮助避免 SQL 注入攻击到达应用。这样,即使应用易受攻击,仍然可以阻止攻击。
有多种方法可以保护应用,但对于这篇文章,我们将重点关注 注入一个 sidecar 容器 来代理所有流量并拒绝任何被检测为 SQLi 攻击的请求。
作为演示,我们使用手动注入 sidecar 的方法,但实际上,手动将代理部署为 sidecar 并不是最好的解决方案。
1. 使用以下内容创建一个名为 2-app-sidecar.yaml
的 YAML 文件,并检查如下重要部分:
• 运行 Pipy 的 sidecar 容器监听 8000 端口
• Pipy 进程将所有流量转发到应用
• 包含 SQLi 的请求 URI 或 POST 正本会被拒绝
• 应用的服务首先将所有流量路由到 Pipy sidecar 容器。
apiVersion: apps/v1
kind: Deployment
metadata:
name: vms
spec:
selector:
matchLabels:
app: vms
template:
metadata:
labels:
app: vms
spec:
containers:
- name: vms
image: naqvis/mamashop
ports:
- containerPort: 80
- name: pipy # <-- sidecar
image: naqvis/pipy-nmi
env:
- name: PIPY_CONFIG_FILE
value: /etc/pipy/nmi/nmi.js
ports:
- containerPort: 8000
volumeMounts:
- mountPath: /etc/pipy/nmi
name: pipy-pjs
volumes:
- name: pipy-pjs
configMap:
name: sidecar
---
apiVersion: v1
kind: Service
metadata:
name: vms
spec:
ports:
- port: 80
targetPort: 8000 # <-- the traffic is routed to the proxy
nodePort: 30060
selector:
app: vms
type: NodePort
---
apiVersion: v1
kind: ConfigMap
metadata:
name: sidecar
data:
nmi.js: |-
pipy({
_rejected: undefined,
})
.import({
__is_sqli: 'lib-inject',
__is_xss: 'lib-inject',
__sqli_fingerprint: 'lib-inject',
})
.listen(8000)
.demuxHTTP().to(
$=>$
.use('/etc/pipy/modules/inject-nmi.so')
.handleMessage(() => _rejected = (__is_sqli || __is_xss))
.branch(
() => _rejected === true, (
$=>$
.handleMessageStart(_ =>
console.log(`SQL Injection found with Fingerprint: ${__sqli_fingerprint}`))
.replaceMessage(new Message({ status: 403 }, 'Forbidden'))
),
() => _rejected === false, (
$=>$.muxHTTP().to(
$=>$.connect('localhost:80')
)
)
)
)
1. 部署应用
$ kubectl apply -f 2-app-sidecar.yaml
等待 pod 启动并准备就绪
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
vms-945f6f85c-8v7sb 2/2 Running 0 8m48s
返回应用页面并再次尝试 SQL 注入来测试 Sidecar 是否正常过滤流量。Pipy sidecar 在请求到达应用程序之前阻止它!
Sidecar 通过返回 403 Forbidden 来阻止流量。使用我们之前尝试过的的 SQLi 来执行更多请求,我们都将得到 403 作为响应。我们可以通过查看 Pipy sidecar 的日志来验证这一点。
学习攻击性技术的目的是使开发人员、防御者和安全专业人员能够保护关键信息资产和服务的机密性、完整性和可用性。
默认情况下,Kubernetes 并不安全。这篇文章使用了 Vulnerable Mama Shop 作为示例应用,这是一个基于 LAMP 技术的简单应用程序,用于演示 SQL 注入攻击以及如何使用 Sidecar 等技术来防御它们。
但随着应用程序和架构复杂性的增加,可能需要对服务进行更细粒度的控制。如果组织需要零信任并且需要像 mTLS 这样的端到端加密,应该考虑像 osm-edge[10] 这样的服务网格,它是一种轻量级、超快速、低资源、高度可扩展的服务网格接口 (SMI) 兼容,专为边缘和云计算服务网格而构建。当在服务之间进行通信(东西向流量)时,服务网格允许控制该级别的流量。
[1]
OWASP Web 应用 10 大安全风险: https://owasp.org/www-project-top-ten/[2]
Pipy: https://flomesh.io/[3]
Native Module Interface (NMI): https://flomesh.io/docs/en/reference/pjs/3-nmi[4]
libinject: https://github.com/libinjection/libinjection[5]
Vulnerable Mama Shop (VMS): https://hub.docker.com/r/naqvis/mamashop[6]
kubectl: https://kubernetes.io/docs/tasks/tools/[7]
pipy-sqli-demo: https://github.com/flomesh-io/pipy-demos/tree/main/pipy-sqli-demo[8]
k3d: https://k3d.io/[9]
k3s: https://github.com/rancher/k3s[10]
osm-edge: https://flomesh.io/