萬(wàn)能密碼的SQL注入漏洞其PHP環(huán)境搭建及防御手段
萬(wàn)能密碼的SQL注入漏洞其PHP環(huán)境搭建及防御手段
一、環(huán)境搭建
這個(gè)滲透環(huán)境的搭建有以下幾點(diǎn):
- 基于session的會(huì)話
- 登錄界面
- 登錄成功界面
- 注銷(xiāo)界面
- 數(shù)據(jù)庫(kù)搭建
- 數(shù)據(jù)庫(kù)連接
二、session會(huì)話
- 服務(wù)器端利用session_start()函數(shù)發(fā)起一次session的會(huì)話
- 此時(shí)我們登錄成功后用戶(hù)的數(shù)據(jù)被保存在服務(wù)器端的Cookie: session= ,即sessionID
- 如果需要再次訪問(wèn)
- 服務(wù)器端的
$_SESSION['...']
會(huì)獲取用戶(hù)session - 然后與原本存在于服務(wù)器的sessionID進(jìn)行比對(duì),如果比對(duì)成功,則證明用戶(hù)正確
三、環(huán)境搭建代碼
1、創(chuàng)建數(shù)據(jù)庫(kù)腳本
在MySQL中使用source命令即可運(yùn)行腳本:
drop database if exists lab; create database lab; use lab; create table users ( id int not null auto_increment, username char(32) not null, passcode char(32) not null, primary key(id) ); insert into users(username,passcode) values('admin','admin123'); insert into users(username,passcode) values('alice','alice456');
2、登錄界面html
<html> <head> <meta charset="UTF-8"> <title>Login</title> <style> #a { width: 500px; text-align: center; } .b { width: 200px; height: 30px; } </style> </head> <body> <div id=a> <h2>Login!</h2> <form name="form_login" method="POST" action="check_login.php"> Username:<input type="text" class="b" name="username" /><br> <br> Password:<input type="password" class="b" name="password" /><br> <input type="submit" name="Submit" value="Submit" /> <input type="reset" name="reset" value="Reset" /> </form> </div> </body> </html>
3、查詢(xún)數(shù)據(jù)庫(kù)是否為正確的賬號(hào)密碼php代碼
<?php include('con_database.php'); $username=isset($_POST['username'])?$_POST['username']:''; $password=isset($_POST['password'])?$_POST['password']:''; if($username=='' || $password==''){ echo "<script>alert('請(qǐng)輸入賬號(hào)和密碼!')</script>"; exit; } $sql="select * from users where username='$username' and passcode='$password'"; $query=mysqli_query($con,$sql) or die('SQL語(yǔ)句執(zhí)行失敗'.mysqli_error($con)); if ($row=mysqli_fetch_array($query)){ session_start(); $_SESSION['username']=$row[1]; echo "<a href='welcome.php'>歡迎訪問(wèn)</a>"; }else{ echo "<script>alert('登錄失??!');history.go(-1)</script>"; } mysqli_close($con); ?>
4、連接數(shù)據(jù)庫(kù)php代碼:
<?php $con=mysqli_connect('127.0.0.1','root','root') or die("數(shù)據(jù)庫(kù)連接失??!"); mysqli_select_db($con,'lab')or die("數(shù)據(jù)庫(kù)連接失敗"); ?>
5、注銷(xiāo)登錄代碼(即關(guān)閉session會(huì)話)
<?php session_start(); session_unset(); session_destroy(); echo "注銷(xiāo)成功"; ?>
6、登錄成功歡迎界面
<?php session_start(); if(isset($_SESSION['username'])){ echo "歡迎用戶(hù)".$_SESSION['username']."登錄"; echo "<br>"; echo "<a href=logout.php>退出登錄</a>"; }else{ echo "您沒(méi)有權(quán)限訪問(wèn)"; } ?>
至此,我們的滲透環(huán)境就構(gòu)建好了
四、萬(wàn)能密碼漏洞剖析
- 用戶(hù)名輸
入' or 1=1 or'
,密碼隨意,發(fā)現(xiàn)可以登錄進(jìn)去 - 密碼輸入
'or '1=1
也可以登錄進(jìn)去
當(dāng)然登錄方法不止一種:
原來(lái)查詢(xún)語(yǔ)句是這樣的:
$sql="select * from users where username='$username' and passcode='$password'";
經(jīng)過(guò)注入之后,變成:
$sql="select * from users where username='' or 1=1 or ' and passcode='****'";
我們觀察到,where后面呃字句中的username被閉合,并且字句分成三個(gè)句子并用or連接。
在SQL語(yǔ)句中 and的優(yōu)先級(jí)要大于or,所以1=1先判斷,為真,即where后面的語(yǔ)句為真,即整個(gè)SQL語(yǔ)句為真,即表示查詢(xún)正確
而形成的語(yǔ)句可以將整個(gè)users表查詢(xún),后面的$row=mysqli_fetch_array($query)
選擇的是查詢(xún)的第一行值,這樣滿(mǎn)足了SQL語(yǔ)句并跳過(guò)了登錄驗(yàn)證
由此可以引申出,只要where后面字句為真,即可跳過(guò)驗(yàn)證,有如下衍生方法:
' or 1=1 #
' or 1=1 --
(后面有空格)'or"="or'
五、萬(wàn)能密碼攻擊防護(hù)
1、使用正則表達(dá)式限制用戶(hù)輸入
可以使用正則表達(dá)式限制用戶(hù)的用戶(hù)名輸入,比如:/^[a-z0-9A-Z_]{5,16}$/
這個(gè)限制了用戶(hù)5位以上16位以下的字母數(shù)字下劃線為用戶(hù)名的輸入
這個(gè)限制在check_login.php中添加
<?php include('con_database.php'); $username=isset($_POST['username'])?$_POST['username']:''; $password=isset($_POST['password'])?$_POST['password']:''; if (!preg_match("/^[a-Z0-9A-Z_]{5,16}$/",$username)){ echo "<script>alert('用戶(hù)名格式錯(cuò)誤')</script>"; exit; if($username=='' || $password==''){ echo "<script>alert('請(qǐng)輸入賬號(hào)和密碼!')</script>"; exit; } $sql="select * from users where username='$username' and passcode='$password'"; $query=mysqli_query($con,$sql) or die('SQL語(yǔ)句執(zhí)行失敗'.mysqli_error($con)); if ($row=mysqli_fetch_array($query)){ session_start(); $_SESSION['username']=$row[1]; echo "<a href='welcome.php'>歡迎訪問(wèn)</a>"; }else{ echo "<script>alert('登錄失敗!');history.go(-1)</script>"; } mysqli_close($con); } ?>
2、使用PHP轉(zhuǎn)義函數(shù)
- addslashes()函數(shù):能夠?qū)我?hào)、雙引號(hào)、反斜杠和null轉(zhuǎn)義
- mysql_escape_string()函數(shù)、mysql_real_escape_string()函數(shù)這個(gè)是轉(zhuǎn)義SQL語(yǔ)句中的符號(hào),php7.x版本的都要變成mysqli
$username=isset($_POST['username'])?addslashes($_POST['username']):''; $password=isset($_POST['password'])?mysqli_real_escape_string($con,$_POST['password']):'';
3、轉(zhuǎn)義函數(shù)的弊端
因?yàn)槭褂玫氖荱TF-8編碼,不是寬字節(jié)編碼,形成的'會(huì)被變成%5c%27
Windows下默認(rèn)的是寬字節(jié)的gbk編碼
如果在%5c前面加上一個(gè)字符形成一個(gè)復(fù)雜的漢字,那么單引號(hào)仍然會(huì)被輸出
六、MySQLi 參數(shù)化查詢(xún)
在使用參數(shù)化查詢(xún)的情況下,服務(wù)器不會(huì)將參數(shù)的內(nèi)容是為SQL指令中的一部分
而是在數(shù)據(jù)庫(kù)完成SQL指令的編譯之后,再代入?yún)?shù)運(yùn)行
此時(shí)就算參數(shù)里面有惡意數(shù)據(jù)
但是此時(shí)SQL語(yǔ)句以及編譯完成
就不會(huì)被數(shù)據(jù)庫(kù)運(yùn)行
PHP提供了三種訪問(wèn)mysql數(shù)據(jù)庫(kù)的拓展:
- MySQL (PHP5.5起,已經(jīng)廢除)
- MySQLi
- PDO(PHP Data Object PHP數(shù)據(jù)對(duì)象)
PDO和MySQLi提供面向?qū)ο蟮腶pi
MySQLi也存在面向過(guò)程的api,所以容易從MySQL轉(zhuǎn)換到MySQLi
下面是mysqli形式的check_login.php 寫(xiě)法,新建check_login_mysqli.php
<?php include('con_database.php'); $username=isset($_POST['username'])?$_POST['username']:''; $password=isset($_POST['password'])?$_POST['password']:''; if($username==''||$password==''){ echo "<script>alert('錯(cuò)誤!');history.go(-1);</script>"; exit; } $sql="select * from users where username=? and passcode=? ;";//問(wèn)號(hào)表示需要一個(gè)參數(shù) $stmt=$con->prepare($sql);//預(yù)編譯SQL語(yǔ)句 if(!$stmt){ echo 'prepare 執(zhí)行錯(cuò)誤'; } else{ $stmt->bind_param("ss",$username,$password); //為預(yù)編譯綁定SQL參數(shù),ss表示兩個(gè)字符串 //i——int d——double s——string b——boolean $stmt->execute(); $result=$stmt->get_result(); $row=$result->fetch_row(); if($row){ session_start(); $_SESSION['username']=$row[1]; echo $row[1]."<a href='welcome.php'>歡迎訪問(wèn)</a>"; }else{ echo "<script>alert('登錄失?。?!');history.go(-1);</script>"; } $stmt->close(); } $con->close(); ?>
一些內(nèi)容已經(jīng)標(biāo)記在代碼的注釋里面
參數(shù)化的PHP代碼真的能夠很有效地防止SQL注入。
以上就是萬(wàn)能密碼的SQL注入漏洞其PHP環(huán)境搭建及防御手段的詳細(xì)內(nèi)容,更多關(guān)于萬(wàn)能密碼的SQL注入 PHP環(huán)境搭建 防御手段的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- PHP基于pdo的數(shù)據(jù)庫(kù)操作類(lèi)【可支持mysql、sqlserver及oracle】
- PHP基于PDO調(diào)用sqlserver存儲(chǔ)過(guò)程通用方法【基于Yii框架】
- PHP6連接SQLServer2005的三部曲
- PHP連接SQLServer2005的方法
- Win2003+apache+PHP+SqlServer2008 配置生產(chǎn)環(huán)境
- php使用pdo連接sqlserver示例分享
- PHP連接SQLServer2005方法及代碼
- Linux下php連接SQLServer 2000數(shù)據(jù)庫(kù)的配置方法
- php5.3中連接sqlserver2000的兩種方法(com與ODBC)
- php插入中文到sqlserver 2008里出現(xiàn)亂碼的解決辦法分享
- PHP連接SQLServer2005的實(shí)現(xiàn)方法(附ntwdblib.dll下載)
- PHP連接SQLSERVER 注意事項(xiàng)(附dll文件下載)
- PHP連接SQLServer2005 的問(wèn)題解決方法
相關(guān)文章
調(diào)整SQLServer2000運(yùn)行中數(shù)據(jù)庫(kù)結(jié)構(gòu)
這篇文章主要介紹了調(diào)整SQLServer2000運(yùn)行中數(shù)據(jù)庫(kù)結(jié)構(gòu),十分實(shí)用的一個(gè)功能,這里推薦給大家,有需要的小伙伴可以參考下。2015-04-04SQL Server 2000“設(shè)備激活錯(cuò)誤”的解決方法
數(shù)據(jù)庫(kù)恢復(fù)時(shí)出現(xiàn)諸如“設(shè)備激活錯(cuò)誤,請(qǐng)使用with move選項(xiàng)來(lái)標(biāo)志該文件的有效位置”報(bào)錯(cuò)的解決方法2013-11-11sqlserver服務(wù)器驗(yàn)證改為混合驗(yàn)證模式步驟
如果在安裝SQL Server數(shù)據(jù)庫(kù)時(shí),一時(shí)疏忽使用了Windows集成驗(yàn)證方式,事后還是可以更改為混合驗(yàn)證模式的,步驟如下2013-12-12SQL Server事務(wù)日志已滿(mǎn)的三種解決方案
我們安裝數(shù)據(jù)庫(kù)后,系統(tǒng)會(huì)默認(rèn)把數(shù)據(jù)庫(kù)文件和數(shù)據(jù)庫(kù)日志文件最大設(shè)為500MB,當(dāng)然你中途可以更改這個(gè)限制,當(dāng)日志文件接近最大值時(shí),繼續(xù)使用數(shù)據(jù)庫(kù)會(huì)提示:事務(wù)日志已滿(mǎn),本文給出了三種解決方案,需要的朋友可以參考下2023-11-11sql where 1=1的優(yōu)缺點(diǎn)分析
where 1=1; 這個(gè)條件始終為T(mén)rue,在不定數(shù)量查詢(xún)條件情況下,1=1可以很方便的規(guī)范語(yǔ)句2013-04-04SQL?Server數(shù)據(jù)庫(kù)判斷最近一次的備份執(zhí)行結(jié)果(最新推薦)
這篇文章主要介紹了SQL?Server數(shù)據(jù)庫(kù)判斷最近一次的備份執(zhí)行結(jié)果,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-05-05SQL Server觸發(fā)器及觸發(fā)器中的事務(wù)學(xué)習(xí)
首先, 說(shuō)下我寫(xiě)篇文章的目的,我希望能把我對(duì)觸發(fā)器的理解,分享出來(lái)與你一起學(xué)習(xí)2011-05-05SQL實(shí)現(xiàn)查詢(xún)某字段的值為空的記錄
這篇文章主要介紹了SQL實(shí)現(xiàn)查詢(xún)某字段的值為空的記錄,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05