有什么好的办法避免程序被SQL注入

对于WEB程序项目,有时候接收的参数变量比较多,如果一个个去check会非常麻烦,请问有没有一种比较简单的方法来避免。

崔苔吾
崔苔吾
7597
编辑于2012-05-14
评论 (0)链接2011-09-13 

小飞的方法很好,数字型和字符型都进行了很好的过滤。除了防SQL注入还要做好防XSS(包括反射型XSS和存储型XSS)、上传漏洞的准备,这几个是常用且有效的攻击手段,我们公司都出现过这几个漏洞,不过修复得也很快。

XSS:又叫CSS (Cross Site Script) ,跨站脚本攻击。它指的是恶意攻击者往Web页面里插入恶意html代码,当用户浏览该页之时,嵌入其中Web里面的html代码会被执行,从而达到恶意攻击用户的特殊目的。XSS属于被动式的攻击,因为其被动且不好利用,所以许多人常忽略其危害性。

  
一般寻找跨站攻击的地方是找有表单输入的地方;然后提交一段脚本语言,比如javascript、vbscript等;这样可以获得用户和管理员的Cookie信息、挂马、钓鱼。

有了管理员的Cookie信息,有时能对web后台操作,发布一些信息获得用户的Cookie或给用户挂马。
挂马是向页面中加入恶意转向代码,当你访问被加入恶意代码的页面时,你就会自动的访问被转向的地址或者下载木马病毒。
防御方法过滤掉"<"、">"、"\"、以及单双引号就行,使用htmlspecialchars函数做过滤。

上传漏洞:主要分为两大类,一类是没有对上传格式做很好的限制,比如linux环境下上传个php的木马,过滤方法不能使用排除法,因为有些扩展名是不能预料到的。比如.php3,.php4也可以当做php网页来执行。要限制住只能使用某某扩展名,比如".JPG"、".BMP"、".GIF"。而且要再服务端做校验,不要紧紧用客户端脚本,因为可以修改发送的封包的,把封包中的muma.php.JPG修改为muma.php。
一类是网页嵌入图片时,没有使用<IMG>标签链接图像地址,而是把图片内容包含在网页中,这样就可以构造恶意的图片内容来搞破坏了,比如:
GIF89a <head> <meta http-equiv = "refresh" content = "1; url=http://www.hacker.com/" />. </head>
访问这个网页会加载图片,就跳到挂有木马的http://www.hacker.com/ 网站了。
这一点,平台部的兄弟们做得很好,不管上传什么,一律把文件生成缩略图快照,两类上传漏洞都防住了。

其它的CSRF、Session Hijacking、HTTP Response Splitting -HTTP响应拆分攻击、Flash溢出跨站等比较难利用,一般不容易中招。

另外防止SQL还需设置php.ini文件中的display_errors = Off,因为通过构造一些恶意的SQL语句造成网页显示错误信息,而错误信息又包括一些敏感的信息,比如数据库表名、字段名等就不好了。

刘锡涛
刘锡涛
281
编辑于 2019-01-05
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2011-10-13

在php里用PDO::prepare来对所以sql进行重新组合,比如:

  
$unsafe_variable = $_POST["input"];
mysql_query("SELECT * FROM employees WHERE name =$unsafe_variable");

上面要做到防注入应该这样重新组合:

  
$preparedStatement = $db->prepare('SELECT * FROM employees WHERE name = :name');
$preparedStatement->execute(array(':name' => $name));
$rows = $preparedStatement->fetchAll();

上面的$db是PDO object

看一下php官方:PDO::prepare

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-03-17

看到的一个WEB SERVER介绍。

Hiawatha是一种能够提供更强安全特性的Unix网络服务器,由Hugo Leisink在2002年开发,如今仍不断升级。上一个稳定的升级版本是v7.0,2010年2月发布。虽然Hiawatha主要为适用PHP环境而改进,它能够支持所有CGI/Fast CGI应用。Hiawatha提供许多安全特性,其中包括:

防止SQL注入
防止CSRF攻击
防止DoS阻断服务攻击
预防黑客潜入
限制CGI应用的运行时间

Hiawatha网络服务器可以运行于Linux、BSD、MacOS X和Windows环境。

http://en.wikipedia.org/wiki/Hiawatha_webserver

崔苔吾
崔苔吾
7597
编辑于 2012-05-14
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2011-09-29

为了防止用户的错误数据和 php + mysql 注入 ,这是我以前常用的一个函数

  
PAPI_GetSafeParam(),用来获取安全的参数值:
define("XH_PARAM_INT",0);
define("XH_PARAM_TXT",1);
function PAPI_GetSafeParam($pi_strName, $pi_Def = "", $pi_iType = XH_PARAM_TXT)
{
if ( isset($_GET[$pi_strName]) )
$t_Val = trim($_GET[$pi_strName]);
else if ( isset($_POST[$pi_strName]))
$t_Val = trim($_POST[$pi_strName]);
else
return $pi_Def;

// INT
if ( XH_PARAM_INT == $pi_iType)
{
if (is_numeric($t_Val))
return $t_Val;
else
return $pi_Def;
}

// String
$t_Val = str_replace("&", "&amp;",$t_Val);
$t_Val = str_replace("<", "&lt;",$t_Val);
$t_Val = str_replace(">", "&gt;",$t_Val);
if ( get_magic_quotes_gpc() )
{
$t_Val = str_replace("\\\"", "&quot;",$t_Val);
$t_Val = str_replace("\\''", "&#039;",$t_Val);
}
else
{
$t_Val = str_replace("\"", "&quot;",$t_Val);
$t_Val = str_replace("'", "&#039;",$t_Val);
}
return $t_Val;
}
  在这个函数中,有三个参数:
$pi_strName: 变量名
$pi_Def: 默认值
$pi_iType: 数据类型。取值为 XH_PARAM_INT, XH_PARAM_TXT, 分别表示数值型和文本型。
  如果请求是数值型,那么调用 is_numeric() 判断是否为数值。如果不是,则返回程序指定的默认值。
  简单起见,对于文本串,我将用户输入的所有危险字符(包括HTML代码),全部转义。由于 php 函数 addslashes()存在漏洞,我用 str_replace()直接替换。get_magic_quotes_gpc() 函数是 php 的函数,用来判断 magic_quotes_gpc 选项是否打开。
  刚才第二节的示例,代码可以这样调用:
<?
if ( isset($_POST["f_login"] ) )
{
// 连接数据库...
// ...代码略...

// 检查用户是否存在
$t_strUid = PAPI_GetSafeParam("f_uid", 0, XH_PARAM_INT);
$t_strPwd = PAPI_GetSafeParam("f_pwd", "", XH_PARAM_TXT);
$t_strSQL = "SELECT * FROM tbl_users WHERE uid=$t_strUid AND password = '$t_strPwd' LIMIT 0,1";
if ( $t_hRes = mysql_query($t_strSQL) )
{
// 成功查询之后的处理. 略...
}
}
?>

  这样的话,就已经相当安全了。PAPI_GetSafeParam的代码有点长,但牺牲这点效率,对保证安全,是值得的。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-05-14

给一个php版本的,不是很全面,不过一般的过滤已经足够了。

  
/**
@param string $val--过滤的参数
@param string $type--过滤类型int|string|html
@param string $method--post|get|request
@return mixed
**/

function filter_val($val,$type='int',$method='post'){
$arr = array();
switch (strtoupper($method)) {
case 'POST':
$arr = $_POST;
break;
case 'GET':
$arr = $_GET;
break;
case 'REQUEST':
$arr = $_REQUEST;
break;
default:
break;
}

if (isset($arr["$val"])) {
if ($type == 'int') {
return intval($arr["$val"])?intval($arr["$val"]):0;
} elseif ($type == 'string') {
return empty($arr["$val"])?false:mysql_escape_string(strip_tags($arr["$val"]));
} elseif ($type == 'html') {
return empty($arr["$val"])?false:addslashes(strip_tags($arr["$val"],"<a><p><font><b>"));
}
} else {
return false;
}
}

以前用过一个雅虎用的专门检测的php的扩展,很繁琐,对于一般的项目没有必要那么严谨。

小白
小白
1776
编辑于 2012-03-31
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (1)链接 • 2011-09-14
  • 0 支持
    还可以加上$_COOKIE和$_SERVER字段 – 黄文彬 2011-10-13

用PDO,所有的sql都要用prepareStatement方法来组合,而不是自己手工组合,不仅安全,而且高效

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (4)链接 • 2011-09-14
  • 0 支持
    非常简洁的方法。 – Geo5 2011-09-14
  • 0 支持
    是java的方法吧。 – Geo5 2011-09-14
  • 1 支持
    php的PDO方法叫prepare,是一个php的扩展 – 小飞 2011-09-14
  • 1 支持
    嗯,类似于Java的JDBC – 潘占东 2011-09-14

一般SQL注入防范对策有如下几点:
1、输入检查,也就是要对传递过来的参数进行检查,过滤非法字符;
2、存储过程,就是建议使用存储过程来代替一些sql语句;
3、参数化SQL,尽量少用字符串拼接的办法来生成sql语句,而是通过prepareStatement之类的方法来进行组合;
4、屏蔽错误信息,就是如果出错或异常就转跳到指定的错误页面,而不是让错误信息直接返回给客户端;
5、数据库权限,合理配置数据库的权限;
6、静态化/伪静态化,能做成静态的尽量做成静态或伪静态。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-05-04

看你的标签是php,那么php有pdo,pdo提供了常见数据库的PDO驱动,比如sql server ,sqlite mysql 。科学高效的做法是使用pdo占位符 ,再存储数据。 相比传统的 过滤注入字符串的做法,它更完整的保留了用户正常的输入“注入字符”

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-03-17

个人认为防注入必须是处处小心,最重要的是养成良好的编码习惯,在写代码的过程中就把风险排除掉,然后加上一些框架的辅助,只能是尽可能的避免注入,如果是线上项目的话,可以用注入探针之类的工具进行测试。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (2)链接 • 2011-09-13
  • 1 支持
    注入探针之类的工具容易误杀 – 程序员1999 2011-09-13
  • 0 支持
    试用过phpids,会把很多正常的请求屏蔽掉。 – 程序员1999 2011-09-13

建议使用PDO操作数据库,不仅防止了SQL注入,而且还可以增加数据库操作的稳定性。

PDO使用prepare和bindValue、bindParam方式操作,会进行SQL预编译。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-06-01

一般说来,用一个好的框架可以解决大部分的XSS和SQL注入漏洞,比如python的django,或者ruby的RoR。
其实如果网站开发人员在安全方面并不专业也没有太多精力去深入研究,也可以考虑使用类似CloudFlare的服务来解决问题,国内创新工场旗下的安全宝就提供这样的服务,等于是在你的网站前面加一个反向代理,替你做CDN加速并过滤XSS及SQL注入攻击。

该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-05-04

我这里也由一些防止SQL注入的函数群,大家也可以看看。呵呵~~

  
/**
* 函数名称:inject_check()
* 函数作用:检测提交的值是不是含有SQL注射的字符,防止注射,保护服务器安全
* 参  数:$sql_str: 提交的变量
* 返 回 值:返回检测结果,ture or false
*/
function inject_check($sql_str) {
return eregi('select|insert|and|or|update|delete|\'|\/\*|\*|\.\.\/|\.\/|union|into|load_file|outfile', $sql_str); // 进行过滤
}

/**
* 函数名称:verify_id()
* 函数作用:校验提交的ID类值是否合法
* 参  数:$id: 提交的ID值
* 返 回 值:返回处理后的ID
*/
function verify_id($id=null) {
if(!$id) { // 是否为空判断
exit('没有提交参数!');
} elseif(inject_check($id)) { // 注射判断
exit('提交的参数非法!');
} elseif(!is_numeric($id)) { // 数字判断
exit('提交的参数非法!');
}
$id = intval($id); // 整型化

return $id;
}

/**
* 函数名称:str_check()
* 函数作用:对提交的字符串进行过滤
* 参  数:$var: 要处理的字符串
* 返 回 值:返回过滤后的字符串
*/
function str_check( $str ) {
if(!get_magic_quotes_gpc()) { // 判断magic_quotes_gpc是否打开
$str = addslashes($str); // 进行过滤
}
$str = str_replace("_", "\_", $str); // 把 '_'过滤掉
$str = str_replace("%", "\%", $str); // 把 '%'过滤掉

return $str;
}

/**
* 函数名称:post_check()
* 函数作用:对提交的编辑内容进行处理
* 参  数:$post: 要提交的内容
* 返 回 值:$post: 返回过滤后的内容
*/
function post_check($post) {
if(!get_magic_quotes_gpc()) { // 判断magic_quotes_gpc是否为打开
$post = addslashes($post); // 进行magic_quotes_gpc没有打开的情况对提交数据的过滤
}
$post = str_replace("_", "\_", $post); // 把 '_'过滤掉
$post = str_replace("%", "\%", $post); // 把 '%'过滤掉
$post = nl2br($post); // 回车转换
$post = htmlspecialchars($post); // html标记转换

return $post;
}
小白
小白
1776
编辑于 2011-11-14
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2011-11-14

标准答案应该是用pdo
不用pdo的话,几乎没办法做到安全,不服气的话去看phpMyAdmin的源代码

至尊宝
至尊宝
1159
编辑于 2011-10-14
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (1)链接 • 2011-10-14
  • 0 支持
    我觉得,pdo也是程序,理论上php完全可以实现它的功能,只有一点效率上可能有差别。
    比较通常的做法是,对数字型直接转换,字符串就采用mysql_real_escape_string
    应该没有什么问题的
    magic_quotes的功能完全是条蛇腿
    – shirne 2012-11-01

写一个通用的方法调用。可以传数组多个处理。很土的方法
例如

function check($arr) {
if(is_array($arr)) {
foreach($arr as $k => $v){
$arr[$k] = strip_tags($v);
}
}else{
return strip_tags($arr);
}
return $arr;
}

不过一般都是采用框架。我目前的项目就是采用zendDB类!

zhupp
zhupp
963
编辑于 2012-03-02
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2011-09-13

这个有个PHP的解决办法 给你 供参考下:

先说明下
magic_quotes_gpc 一直是个头疼的问题 无论 是否开启 都会影响项目的可移植性

所以 php 5.3.0以后 去掉了 这个功能(php 5.3.0以后使用magic_quotes_gpc 和 magic_quotes_runtime 会发出Warning 警告,php6.0以后会测底删除这2个功能。magic_quotes_gpc 中的 gpc 是指 $_GET,$_POST,$_COOKIE 中的 值 这三个值 涉及到 sql注入的问题,magic_quotes_runtime是指php程序从文件或是其他地方获取到的值是否转义 这里不讨论magic_quotes_runtime的问题 ,个人觉得只要程序员稍微留意下 这个问题不存在)

下面这个函数 可以解决这个问题 无论php版本是否大于5.3.0 或是当php版本小于5.3.0时 是否开启magic_quotes_gpc功能 统统不用考虑

这个函数会自动转义 get,post,cookie 中的 单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)。

只要在获取 get,post , cookie 前运行下这个函数即可 并且这个函数带有 缓存记忆功能 (一般在头部 功能 程序中 执行下 这个函数就行)。

调用的时 直接 set_magic_quotes_gpc() 不用传递任何参数

  
function set_magic_quotes_gpc($arr=NULL){

static $isset = 1;
if($isset==2) return ;
if($isset==1){
if (version_compare(PHP_VERSION, '5.3.0','<')) {
if(get_magic_quotes_gpc()==1){
$isset = 2;
return ;
}
}
$isset = 3;
}
$funcName = __FUNCTION__;
if($arr!==NULL){
foreach($arr as &$val){
if(is_array($val)){
$val=$funcName($val);
}else{
$val = addslashes($val);
}

}
return $arr;
}
//GET 过滤
foreach($_GET as &$val){
if(is_array($val)){
$val=$funcName($val);
}else{
$val = addslashes($val);
}
}
//POST 过滤
foreach($_POST as &$val){
if(is_array($val)){
$val=$funcName($val);
}else{
$val = addslashes($val);
}
}
//COOKIE 过滤
foreach($_COOKIE as &$val){
if(is_array($val)){
$val=$funcName($val);
}else{
$val = addslashes($val);
}
}

}
该答案已被锁定,无法对其进行评论,编辑及投票。
()
评论 (0)链接 • 2012-07-26
德问是一个专业的编程问答社区,请 登录注册 后再提交答案