web网站攻击方式多种多样,sql注入是经常使用的攻击方向。攻击者把SQL命令插入到Web表单的输入域或页面请求的字符串,欺骗服务器执行恶意的SQL命令 SQL注入不是Web或数据库服务器中的缺陷,而是由于编程实践较差且缺乏经验而导致的。
动态拼接的SQL指令最易受攻击。
如登录时使用的SQL指令:
$query = 'SELECT * from Users WHERE login = ' $username ' AND password = ' $password'
攻击者主要利用了mysql的注释指令:# 或者 --
攻击者在帐户文本框中输入 aaa' or 1=1 #, 即可骗过服务器,成功登录
详情情况看下面的代码:
数据库
连接数据库的conn.php
<?php
$mysqli = new mysqli('localhost','root','root','demo');
if($mysqli->connect_errno){
printf("连接数据库失败: %s\n", $mysqli->connect_error);
exit();
}
$mysqli->query('set names utf8');
用户登录 login.php
<?php
if(empty($_POST)){
session_start();
unset($_SESSION);
session_destroy();
header('location:login.html');
exit();
}
require_once 'conn.php';
$username = $_POST['username'];
$password = $_POST['password'];
$password = md5(md5($password));
//错误的sql语句
$sql = "select username from users where username='{$username}' and password='{$password}'";
$result = $mysqli->query($sql);
if($result === false) {
die($mysqli->error);
}
if($arr = $result->fetch_array()){
session_start();
$_SESSION['username'] = $username;
header('location:index.php');
}else{
$msg = '用户名或密码输入错误,重新输入';
$wait = 3;
$url = 'login.html';
require_once 'jump.php';
exit();
}
页面跳转 jump.php
<!doctype html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"/>
<title>跳转提示</title>
<style type="text/css">
*{ padding: 0; margin: 0; }
body{ background: #fff; font-family: "Microsoft Yahei","Helvetica Neue",Helvetica,Arial,sans-serif; color: #333; font-size: 16px; }
.system-message{ padding: 24px 48px; }
.system-message h1{ font-size: 100px; font-weight: normal; line-height: 120px; margin-bottom: 12px; }
.system-message .jump{ padding-top: 10px; }
.system-message .jump a{ color: #333; }
.system-message .success,.system-message .error{ line-height: 1.8em; font-size: 36px; }
.system-message .detail{ font-size: 12px; line-height: 20px; margin-top: 12px; display: none; }
</style>
</head>
<body>
<div class="system-message">
<?php switch ($code) {?>
<?php case 1:?>
<h1>:)</h1>
<p class="success"><?php echo(strip_tags($msg));?></p>
<?php break;?>
<?php case 0:?>
<h1>:(</h1>
<p class="error"><?php echo(strip_tags($msg));?></p>
<?php break;?>
<?php } ?>
<p class="detail"></p>
<p class="jump">
页面自动 <a id="href" href="<?php echo($url);?>">跳转</a> 等待时间: <b id="wait"><?php echo($wait);?></b>
</p>
</div>
<script type="text/javascript">
(function(){
var wait = document.getElementById('wait'),
href = document.getElementById('href').href;
var interval = setInterval(function(){
var time = --wait.innerHTML;
if(time <= 0) {
location.href = href;
clearInterval(interval);
};
}, 1000);
})();
</script>
</body>
</html>
前端登录页面:login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>用户登录</title>
<link rel="stylesheet" href="static/bootstrap-3.3.7-dist/css/bootstrap.css">
<style>
body {
background-color: #cecece;
}
.login-form {
background-color: #fff;
height: 500px;
padding: 20px;
border-radius: 1em;
margin: 50vh auto 0;
transform: translateY(-50%);
-moz-box-shadow: 2px 2px 5px #333333;
-webkit-box-shadow: 2px 2px 5px #333333;
box-shadow: 2px 2px 5px #333333;
}
.login-form header {
font-size: 36px;
font-weight: bolder;
text-align: center;
margin-bottom: 30px;
}
.login-form .btn-row{
margin-top: 30px;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-10 col-md-offset-1">
<form class="login-form" action="login.php" method="post">
<header>用户登录</header>
<div class="form-group">
<label for="username">用户名</label>
<input type="text" class="form-control" id="username" name="username" placeholder="输入用户名">
</div>
<div class="form-group">
<label for="password">密码</label>
<input type="text" class="form-control" id="password" name="password" placeholder="输入密码">
</div>
<div class="form-group btn-row">
<button type="submit" class="btn btn-primary btn-block">登录</button>
</div>
<p class="text-primary">本案例演示sql注入</p>
<p>sql注入1:利用mysql 注释,用户名输入aaa' or 1=1 #, 密码随意输入</p>
</form>
</div>
</div>
</div>
<script src="static/jquery-1.11.3.min.js"></script>
<script src="static/bootstrap-3.3.7-dist/js/bootstrap.js"></script>
</body>
</html>
登录成功后进入首页:index.php
<?php
session_start();
if(empty($_SESSION['username'])){
$msg = '没有权限,请先登录';
$wait = 3;
$url = 'login.html';
require_once 'jump.php';
exit();
}
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<p>欢迎:<?php echo $_SESSION['username']; ?></p>
<p><a href="logout.php">注销</a> </p>
</body>
</html>
注销 logout.php
<?php
session_start();
unset($_SESSION);
session_destroy();
header('location:login.html');
exit();
最后实现的效果是:
攻击者利用程序上的漏洞,轻而易举登录成功。
使用sql 指令的参数化可防范SQL注入
正确的login.php代码如下 :
<?php
if(empty($_POST)){
session_start();
unset($_SESSION);
session_destroy();
header('location:login.html');
exit();
}
require_once 'conn.php';
$username = $_POST['username'];
$password = $_POST['password'];
$password = md5(md5($password));
//正确的sql
$sql = "select username from users where username=? and password=?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('ss',$username,$password);
$stmt->execute();
if($stmt->affected_rows){
$result = $stmt->get_result();
if ($arr = $result->fetch_array()) {
session_start();
$_SESSION['username'] = $username;
header('location:index.php');
} else {
$msg = '用户名或密码输入错误,重新输入';
$wait = 3;
$url = 'login.html';
require_once 'jump.php';
exit();
}
}
如果同样输入:aaa' or 1=1 #
得到的结果是: