避免mysql注入应该避免都有哪些特殊字符

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了避免mysql注入应该避免都有哪些特殊字符相关的知识,希望对你有一定的参考价值。

参考技术A

特殊字符有:

SQL中通配符的使用


SQL注入式攻击,就是攻击者把SQL命令插入到Web表单的输入域或页面请求的查询字符串,欺骗服务器执行恶意的SQL命令。在某些表单中,用户输入的内容直接用来构造(或者影响)动态SQL命令,或作为存储过程的输入参数,这类表单特别容易受到SQL注入式攻击。

参考技术B 你可以学习一下如何防止mysql被注入。
一、SQL注入简介
SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编程时的疏忽,通过SQL语句,实现无帐号登录,甚至篡改数据库。

二、SQL注入攻击的总体思路
1.寻找到SQL注入的位置
2.判断服务器类型和后台数据库类型
3.针对不通的服务器和数据库特点进行SQL注入攻击

三、SQL注入攻击实例
比如在一个登录界面,要求输入用户名和密码:
可以这样输入实现免帐号登录:
用户名: ‘or 1 = 1 –
密 码:
点登陆,如若没有做特殊处理,那么这个非法用户就很得意的登陆进去了.(当然现在的有些语言的数据库API已经处理了这些问题)
这是为什么呢? 下面我们分析一下:
从理论上说,后台认证程序中会有如下的SQL语句:
String sql = "select * from user_table where username=
' "+userName+" ' and password=' "+password+" '";
当输入了上面的用户名和密码,上面的SQL语句变成:
SELECT * FROM user_table WHERE username=
'’or 1 = 1 -- and password='’
分析SQL语句:
条件后面username=”or 1=1 用户名等于 ” 或1=1 那么这个条件一定会成功;
然后后面加两个-,这意味着注释,它将后面的语句注释,让他们不起作用,这样语句永远都能正确执行,用户轻易骗过系统,获取合法身份。
这还是比较温柔的,如果是执行
SELECT * FROM user_table WHERE
username='' ;DROP DATABASE (DB Name) --' and password=''
….其后果可想而知…

四、应对方法
下面我针对JSP,说一下应对方法:
1.(简单又有效的方法)PreparedStatement
采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setXXX方法传值即可。
使用好处:
(1).代码的可读性和可维护性.
(2).PreparedStatement尽最大可能提高性能.
(3).最重要的一点是极大地提高了安全性.
原理:
sql注入只对sql语句的准备(编译)过程有破坏作用
而PreparedStatement已经准备好了,执行阶段只是把输入串作为数据处理,
而不再对sql语句进行解析,准备,因此也就避免了sql注入问题.

2.使用正则表达式过滤传入的参数
要引入的包:
import java.util.regex.*;
正则表达式:
private String CHECKSQL = “^(.+)\\sand\\s(.+)|(.+)\\sor(.+)\\s$”;
判断是否匹配:
Pattern.matches(CHECKSQL,targerStr);
下面是具体的正则表达式:
检测SQL meta-characters的正则表达式 :
/(\%27)|(\’)|(\-\-)|(\%23)|(#)/ix
修正检测SQL meta-characters的正则表达式 :/((\%3D)|(=))[^\n]*((\%27)|(\’)|(\-\-)|(\%3B)|(:))/i
典型的SQL 注入攻击的正则表达式 :/\w*((\%27)|(\’))((\%6F)|o|(\%4F))((\%72)|r|(\%52))/ix
检测SQL注入,UNION查询关键字的正则表达式 :/((\%27)|(\’))union/ix(\%27)|(\’)
检测MS SQL Server SQL注入攻击的正则表达式:
/exec(\s|\+)+(s|x)p\w+/ix
等等…..

3.字符串过滤
比较通用的一个方法:
(||之间的参数可以根据自己程序的需要添加)
public static boolean sql_inj(String str)

String inj_str = "'|and|exec|insert|select|delete|update|
count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";
String inj_stra[] = split(inj_str,"|");
for (int i=0 ; i < inj_stra.length ; i++ )

if (str.indexOf(inj_stra[i])>=0)

return true;


return false;


4.jsp中调用该函数检查是否包函非法字符

防止SQL从URL注入:
sql_inj.java代码:

package sql_inj;
import java.net.*;
import java.io.*;
import java.sql.*;
import java.text.*;
import java.lang.String;
public class sql_inj
public static boolean sql_inj(String str)

String inj_str = "'|and|exec|insert|select|delete|update|
count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|,";
//这里的东西还可以自己添加
String[] inj_stra=inj_str.split("\\|");
for (int i=0 ; i < inj_stra.length ; i++ )

if (str.indexOf(inj_stra[i])>=0)

return true;


return false;



5.JSP页面判断代码:

使用javascript在客户端进行不安全字符屏蔽
功能介绍:检查是否含有”‘”,”\\”,”/”
参数说明:要检查的字符串
返回值:0:是1:不是
函数名是
function check(a)

return 1;
fibdn = new Array (”‘” ,”\\”,”/”);
i=fibdn.length;
j=a.length;
for (ii=0; ii<i; ii++)
for (jj=0; jj<j; jj++)
temp1=a.charAt(jj);
temp2=fibdn[ii];
if (tem’; p1==temp2)
return 0;


return 1;

===================================
总的说来,防范一般的SQL注入只要在代码规范上下点功夫就可以了。
凡涉及到执行的SQL中有变量时,用JDBC(或者其他数据持久层)提供的如:PreparedStatement就可以 ,切记不要用拼接字符串的方法就可以了。本回答被提问者采纳

避免对 Node.js 中的存储过程进行 SQL 注入

【中文标题】避免对 Node.js 中的存储过程进行 SQL 注入【英文标题】:Avoiding SQL Injections to the Stored Procedure in Node.js 【发布时间】:2019-06-26 18:45:55 【问题描述】:

在调用存储过程时如何避免来自 Node.js 的 SQL 注入

假设我在 UI 的前端输入了一些特殊字符 例如:

如果输入?true将被保存到数据库中

如果输入??`true`将被保存到数据库中

对于一些特殊字符,例如反斜杠(\)和撇号('

我将从控制台收到这些类型的错误

From console: '
 Error: ER_PARSE_ERROR: You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near
 '''')' at line 1
    at Query.Sequence._packetToError (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Sequence.js:47:14)
    at Query.ErrorPacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Query.js:77:18)
    at Protocol._parsePacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:278:23)
    at Parser.write (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Parser.js:76:12)
    at Protocol.write (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:38:16)
    at Socket.<anonymous> (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:91:28)
    at Socket.<anonymous> (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:502:10)
    at Socket.emit (events.js:182:13)
    at addChunk (_stream_readable.js:283:12)
    at readableAddChunk (_stream_readable.js:264:11)
    --------------------
    at Protocol._enqueue (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:144:48)
    at Connection.query (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:200:25)
    at Object.saveFeeds (C:\xampp\htdocs\nodechat\middleware\db.js:96:15)
    at C:\xampp\htdocs\nodechat\middleware\routes.js:187:12
    at Layer.handle [as handle_request] (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\layer.js:95:5)
    at next (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\route.js:137:13)
    at Route.dispatch (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\route.js:112:3)
    at Layer.handle [as handle_request] (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\layer.js:95:5)
    at C:\xampp\htdocs\nodechat\node_modules\express\lib\router\index.js:281:22
    at Function.process_params (C:\xampp\htdocs\nodechat\node_modules\express\lib\router\index.js:335:12)
  code: 'ER_PARSE_ERROR',
  errno: 1064,
  sqlMessage:
   'You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near \'\'\'\')\' at line
1',
  sqlState: '42000',
  index: 0,
  sql: 'CALL AddFeedItems(1,\'\'\')' 
undefined
C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Parser.js:80
        throw err; // Rethrow non-MySQL errors
        ^

TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be one of type string or Buffer. Received type undefined
    at write_ (_http_outgoing.js:595:11)
    at ServerResponse.write (_http_outgoing.js:567:10)
    at C:\xampp\htdocs\nodechat\middleware\routes.js:188:17
    at Query.<anonymous> (C:\xampp\htdocs\nodechat\middleware\db.js:100:13)
    at Query.<anonymous> (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:502:10)
    at Query._callback (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\Connection.js:468:16)
    at Query.Sequence.end (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Sequence.js:83:24)
    at Query.ErrorPacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\sequences\Query.js:90:8)
    at Protocol._parsePacket (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Protocol.js:278:23)
    at Parser.write (C:\xampp\htdocs\nodechat\node_modules\mysql\lib\protocol\Parser.js:76:12)

routes.js

app.post('/AddFeedItems', function(req, res) 
        // console.log(req.body);
        try
            console.log(JSON.parse(Object.keys(req.body)[0]));
            req.body = JSON.parse(Object.keys(req.body)[0]);
         catch(err) 
            console.log('Error');
            req.body = req.body
        
        db.saveFeeds(req.body, function(chats) 
            res.write(JSON.stringify(chats));
            res.end();
        )
);

db.js

function saveFeeds(data,cb) 
    const conn = createConnection();
        conn.connect();
        console.log('From console'+data.keyword);
        let  sql ="CALL AddFeedItems("+data.senderid + ",'" + data.keyword + "')";
         conn.query(sql, true,function(err,result) 
           if(err) console.log(err);
            conn.end();
             console.log(result);
            cb(result); 
        );

MySQL 存储过程

CREATE PROCEDURE `AddFeedItems`(IN `senderid` BIGINT(255), IN `keyword` VARCHAR(255)) NOT DETERMINISTIC CONTAINS SQL SQL SECURITY DEFINER 
BEGIN 
DECLARE LastFeedId INT; 
INSERT INTO `feed_item` (`userid`, `content`, `timestamp`, `likes`, `comments`, `user_flag`, `likes_data`) VALUES (senderid, keyword, CURRENT_TIMESTAMP(), 0, 0, 0, 'like'); 
SET LastFeedId = LAST_INSERT_ID(); 
INSERT INTO `feed_item_likes` (`feed_item_id`, `user_id`, `timestamp`, `is_like`) VALUES (LastFeedId, senderid, CURRENT_TIMESTAMP(), 0); 
SELECT LastFeedId; 
END

一般来说,为了防止这些特殊字符注入到 SQL 中。代码中有哪些必要的步骤和注意事项?


编辑:

需要一个函数来去除一组特殊字符,如反斜杠(\)、美元($)、撇号(@987654333 @) 和问号 (?) 来自 Node.js

所以我找到了替换所有上述特殊字符的解决方案,除了问号(?)。

获得的结果如下:

正则表达式中有问号

data.keyword.replace(/[\\$'"\?]/g, "\\$&amp;")

+-----------+-----------+
|Entered    |Saved into |
|Character  |Database   |
+-----------+-----------+
| ?         | rue       |
| ??        | rue?      |
| ???       | ???       |
+-----------+-----------+

正则表达式中没有问号

data.keyword.replace(/[\\$'"]/g, "\\$&amp;")

+-----------+-----------+
|Entered    |Saved into |
|Character  |Database   |
+-----------+-----------+
| ?         | true      |
| ??        | `true`    |
| ???       | ???       |
+-----------+-----------+

试用内置函数,

    var key = mysql.escape(data.keyword); var key = conn.escape(data.keyword);
let sql ="CALL AddFeedFriendItems("+data.senderid + "," + data.friendid + ",'" + data.friendusername + "'," + key + ")";

结果仍然不理想,问号(?) 将存储为true

我只需要用 ? 值替换那些问号 (?)。相反,truerue 值将存储到数据库中。

我应该如何编写正则表达式来匹配问号并仅替换为相同的字符?


不使用 MySQL 存储过程,将上面的 saveFeeds() 替换为 db.js

数据将以适当的方式存储

function saveFeeds(data,cb)
    const conn = createConnection();
        conn.connect();
        conn.query(
            "INSERT INTO feed_item (userid, content, timestamp, likes, comments, user_flag, likes_data) VALUES (?, ?, ?, ?, ?, ?, ?)", [data.senderid, data.keyword, data.timestamp, 0, 0, 0, 'like'],
            function (err, rows) 
                if(err) 
                    console.log(err);
                 else 
                    var feedId = rows.insertId;
                    var feedId = rows.insertId;
                    conn.query(
                        "INSERT INTO feed_item_likes (feed_item_id, user_id, timestamp, is_like) VALUES (?, ?, ?, ?)", [feedId, data.senderid, data.timestamp, 0],
                        function (err, rows) 
                            if(err) 
                                console.log(err);
                             else 
                                var feedId = rows.insertId;
                            
                        
                    );
                
                conn.end();
                cb(feedId);
            
          );

【问题讨论】:

【参考方案1】:

问题不在于您的存储过程,而在于您的 CALL 语句:

sql: 'CALL AddFeedItems(1,\'\'\')' 

这会产生一条 SQL 语句:

CALL AddFeedItems(1,''')

连续三个' 引号是无效的语法。它在 CALL 语句上抛出语法错误,它永远不会超过运行存储过程。

如果你想要一个包含文字单引号的带引号的字符串,SQL 必须是以下形式之一:

CALL AddFeedItems(1,'''') -- two single-quotes become one literal single-quote 
CALL AddFeedItems(1,'\'') -- escaped single-quote
CALL AddFeedItems(1,"'") -- delimit by alternative quotes, if sql_mode is not ANSI

【讨论】:

您好 Karwin,作为 Oracle ACE 总监,您能否建议我进行任何需要适应我的帖子回答的更改,并感谢您分析根本原因。【参考方案2】:

另外,我找到了正则表达式 + Unicode + 内置函数的解决方案

var key = conn.escape(data.keyword);
var keyword = key.replace(/[?]/g, "❓");

let  sql ="CALL AddFeedFriendItems("+data.senderid + "," + data.friendid + ",'" + data.friendusername + "'," + keyword + ")"; 

List of Unicode Characters with Question Mark

默认情况下,排序规则将在 MySQL 中设置为 latin1_swedish_ci。这会混淆是否要保存哪种类型的字符。所以很自然,它会以纯文本格式?保存。虽然通常对于任何 Unicode 字符 ? 都会保存在数据库中。由于排序规则设置为latin1_swedish_ci

注意:需要一些关于将 Unicode 字符保存到 MySQL 中的研究会阻碍性能或通过 B-Tree 索引检索记录会引发任何其他问题。因为当存储超过 999K 消息时,.replace() 会进一步降低性能。因为 默认情况下,排序规则将在 MySQL 中设置为 latin1_swedish_ci

【讨论】:

以上是关于避免mysql注入应该避免都有哪些特殊字符的主要内容,如果未能解决你的问题,请参考以下文章

php mysql转义特殊字符的函数都有哪些

避免对 Node.js 中的存储过程进行 SQL 注入

json特殊字符都有哪些

addslashes,htmlspecialchars,htmlentities转换或者转义php特殊字符防止xss攻击以及sql注入

正则表达式验证规则,以避免特殊字符不适用于 laravel 中的文本字段

shell脚本 都有哪些特殊字符不支持