整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

网站防止SQL注入的几种方法

网站防止SQL注入的几种方法

QL注入是网站常见的黑客攻击行为之一,相信各大站长对此并不陌生,但是很多站长初遇SQL攻击时都会感到不知所措。本文将对SQL进行一些资料的收集,对针对SQL转入提出一些对应的解决方案,希望对各大站长有所帮助。

什么是SQL注入

SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。具体来说,它是利用现有应用程序,将(恶意的)SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。比如先前的很多影视网站泄露VIP会员密码大多就是通过WEB表单递交查询字符暴出的,这类表单特别容易受到SQL注入式攻击。

如何判断SQL注入漏洞

经专业人士总结,有以下两种方法可以判断网站是否被SQL注入

1、错误提示

如果WEB网站开启了错误显示,攻击者就可以通过反复调整发送的参数、查看页面打印的错误信息,推测出WEB网站使用的数据库和开发语言等重要信息。

2、盲注

除非是运维人员疏忽,否则大多数的WEB运营网站应该都关闭错误提示信息,此时攻击者一般会采用盲注的技巧来进行反复的尝试判断。如果查看的网页页面的url地址为:userinfo.php?username=plhwin,此刻黑客分别访问userinfo.php?username=plhwin’AND 1=1–hack和userinfo.php?username=plhwin’AND 1=2–hack,如果前者访问能返回正常的信息而后者不能,就基本可以判断此网站存在SQL注入漏洞,因为后者的1=2这个表达方式永远不成立,所以即使username传入了正确的参数也无法通过,由此可以推断这个页面存在SQL注入漏洞,并且可以通过username参数进行注入。

网站如何防止SQL注入

1普通用户与系统管理员用户的权限要有严格的区分。

如果一个普通用户在使用查询语句中嵌入另一个Drop Table语句,那么是否允许执行呢?由于Drop语句关系到数据库的基本对象,故要操作这个语句用户必须有相关的权限。在权限设计中,对于终端用户,即应用软件的使用者,没有必要给他们数据库对象的建立、删除等权限。那么即使在他们使用SQL语句中带有嵌入式的恶意代码,由于其用户权限的限制,这些代码也将无法被执行。故应用程序在设计的时候,

2强制使用参数化语句。

如果在编写SQL语句的时候,用户输入的变量不是直接嵌入到SQL语句。而是通过参数来传递这个变量的话,那么就可以有效的防治SQL注入式攻击。也就是说,用户的输入绝对不能够直接被嵌入到SQL语句中。与此相反,用户的输入的内容必须进行过滤,或者使用参数化的语句来传递用户输入的变量。参数化的语句使用参数而不是将用户输入变量嵌入到SQL语句中。采用这种措施,可以杜绝大部分的SQL注入式攻击。不过可惜的是,现在支持参数化语句的数据库引擎并不多。不过数据库工程师在开发产品的时候要尽量采用参数化语句。

3多使用SQL Server数据库自带的安全参数。

为了减少注入式攻击对于SQL Server数据库的不良影响,在SQLServer数据库专门设计了相对安全的SQL参数。在数据库设计过程中,工程师要尽量采用这些参数来杜绝恶意的SQL注入式攻击。

如在SQL Server数据库中提供了Parameters集合。这个集合提供了类型检查和长度验证的功能。如果管理员采用了Parameters这个集合的话,则用户输入的内容将被视为字符值而不是可执行代码。即使用户输入的内容中含有可执行代码,则数据库也会过滤掉。因为此时数据库只把它当作普通的字符来处理。使用Parameters集合的另外一个优点是可以强制执行类型和长度检查,范围以外的值将触发异常。如果用户输入的值不符合指定的类型与长度约束,就会发生异常,并报告给管理员。如上面这个案例中,如果员工编号定义的数据类型为字符串型,长度为10个字符。而用户输入的内容虽然也是字符类型的数据,但是其长度达到了20个字符。则此时就会引发异常,因为用户输入的内容长度超过了数据库字段长度的限制。

4加强对用户输入的验证。

总体来说,防治SQL注入式攻击可以采用两种方法,一是加强对用户输入内容的检查与验证;二是强迫使用参数化语句来传递用户输入的内容。在SQLServer数据库中,有比较多的用户输入内容验证工具,可以帮助管理员来对付SQL注入式攻击。测试字符串变量的内容,只接受所需的值。拒绝包含二进制数据、转义序列和注释字符的输入内容。这有助于防止脚本注入,防止某些缓冲区溢出攻击。测试用户输入内容的大小和数据类型,强制执行适当的限制与转换。这即有助于防止有意造成的缓冲区溢出,对于防治注入式攻击有比较明显的效果。

如可以使用存储过程来验证用户的输入。利用存储过程可以实现对用户输入变量的过滤,如拒绝一些特殊的符号。如以上那个恶意代码中,只要存储过程把那个分号过滤掉,那么这个恶意代码也就没有用武之地了。在执行SQL语句之前,可以通过数据库的存储过程,来拒绝接纳一些特殊的符号。在不影响数据库应用的前提下,应该让数据库拒绝包含以下字符的输入。如分号分隔符,它是SQL注入式攻击的主要帮凶。如注释分隔符。注释只有在数据设计的时候用的到。一般用户的查询语句中没有必要注释的内容,故可以直接把他拒绝掉,通常情况下这么做不会发生意外损失。把以上这些特殊符号拒绝掉,那么即使在SQL语句中嵌入了恶意代码,他们也将毫无作为。

故始终通过测试类型、长度、格式和范围来验证用户输入,过滤用户输入的内容。这是防止SQL注入式攻击的常见并且行之有效的措施。

5多层环境如何防治SQL注入式攻击?

在多层应用环境中,用户输入的所有数据都应该在验证之后才能被允许进入到可信区域。未通过验证过程的数据应被数据库拒绝,并向上一层返回一个错误信息。实现多层验证。对无目的的恶意用户采取的预防措施,对坚定的攻击者可能无效。更好的做法是在用户界面和所有跨信任边界的后续点上验证输入。如在客户端应用程序中验证数据可以防止简单的脚本注入。但是,如果下一层认为其输入已通过验证,则任何可以绕过客户端的恶意用户就可以不受限制地访问系统。故对于多层应用环境,在防止注入式攻击的时候,需要各层一起努力,在客户端与数据库端都要采用相应的措施来防治SQL语句的注入式攻击。


SSL证书采用了技术含量比较高的加密技术。日后GDCA(数安时代)将会持续为大家推荐更多关于SSL证书的技术知识。让大家正确认识SSL证书,快速无误部署HTTPS安全协议。更多资讯,请关注GDCA。

文章转载:https://www.trustauth.cn/wiki/18364.html

、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就可以 ,切记不要用拼接字符串的方法就可以了。

图片来源:unsplash.com[1]]

前言

Node.js和npm为前端生态中提供了统一的开发语言、强大的包管理和模块生态系统、灵活的构建工具和任务自动化、以及丰富的前端框架和库等等。

可以说,正是因为nodejs带来的这些工具和资源使前端开发更加高效、便捷,并推动了前端技术的快速发展。

但是近年来,Node.js 生态系统中的 npm 软件包中出现了许多 CVE("Common Vulnerabilities and Exposures" 常见漏洞和公开漏洞),譬如lodash库的CVE漏洞——CVE-2018-16487[2]、express库的CVE漏洞——CVE-2018-17346[3]以及jsonwebtoken库的CVE漏洞——CVE-2018-12424[4]等等,在这其中有一个特别危险且屡禁不止的漏洞就是命令注入(Command Injection)

作为前端工程师而言,在我们日常工作中,不仅需要快速交付、优化性能相关,还要时刻对项目中所采用的nodejs技术栈及其安全相关的因素考虑在内。

简而言之,关于安全这根弦儿得时刻紧绷着!

命令注入[5]是一种攻击,其目的是通过有漏洞的应用程序在主机操作系统上执行任意命令。当应用程序将用户提供的不安全数据(表单、cookie、HTTP 标头等)传递给系统shell时,就有可能发生命令注入攻击。在这种攻击中,攻击者提供的操作系统命令通常是以受攻击应用程序的权限执行的。命令注入攻击之所以可能,主要是因为输入校验不足。

这种攻击与代码注入不同,代码注入允许攻击者添加自己的代码,然后由应用程序执行。在命令注入攻击中,攻击者扩展应用程序的默认功能,执行系统命令,而无需注入代码。

场景分析

假设有某程序员a同学在某个nodejs项目中写出了类似的代码:

const { exec }=require('child_process');

function runCommand(userInput) {
  const command=`ls ${userInput}`; // 将用户所输入的内容拼接到命令中
  exec(command, (error, stdout, stderr)=> {
    if (error) {
      console.error(`执行命令时出错:${error}`);
      return;
    }
    console.log(`命令执行结果:${stdout}`);
  });
}

const userInput='; rm -rf /'; // 恶意用户所输入的内容

runCommand(userInput);

我们简单分析下以上代码,这段程序可以将用户所输入的内容直接拼接到命令行字符串中。如果因为项目工期紧张,没经过code review匆忙上线,恰好碰到个别用户所输入的恶意的命令,例如'; rm -rf /',那么最终执行的命令将变为ls ; rm -rf /,导致“删库跑路”的危险操作。

当然这只是为了举例的简单例子,实际项目中,发生命令注入的原因大多是没有对用户所输入的内容进行严谨的校验

命令注入 - 常见威胁

命令注入是 Node.js 生态系统中真实而普遍的威胁。

看似显而易见的安全风险,如以下代码所示:

var exec=require('child_process').execSync
var platform=require('os').platform()

module.exports=function(){
  var commands=Array.isArray(arguments[0]) ? arguments[0] : Array.prototype.slice.apply(arguments)
  var command=null
  commands.some(function(c){
    if (isExec(findCommand(c))){
      command=c
      return true
    }
  })
  return command
}

function isExec(command){
  try{
    exec(command, { stdio: 'ignore' })
    return true
  }
  catch (_e){
    return false
  }
}
function findCommand(command){
  if (/^win/.test(platform)){
    return "where " + command
  } else {
    return "command -v " + command
  }
}

上述命令注入漏洞是在 find-exec[6] npm 软件包中发现的,该软件包每周的下载量多达 2000 多次。虽然数量不多,但足以让一些用户面临风险。命令注入漏洞的后果可能是毁灭性的,从数据泄露到系统完全崩溃不等。

现在我们再回过头来看,到底什么是命令注入[7]?简而言之,命令注入的核心是应用程序允许未经审核的用户所输入的内容作为系统命令执行。这些命令可操纵底层系统,可能导致未经授权的访问、数据泄露,甚至完全破坏系统。

fs-git

在另外一个案例中,我们可以看下fs-git npm 软件包(版本 1.0.1)这个看似无害的模块是如何成为一个严重的安全隐患的:

fs-git 是 Node.js 的一个 npm 包,能够为 Git 仓库提供类似于文件系统的 API,进而可以让开发人员更直观、更容易地与 Git 仓库交互。它拥有相当数量的用户群体,所以该安全隐患所造成的影响可见一斑。

在1.0.1 版本的 fs-git 模块中,被发现了编号为 CVE-2017-1000451[8]漏洞。该模块依赖 child_process.exec 函数来执行系统命令。然而,用于构建执行字符串的 buildCommand函数缺少严谨的校验逻辑,使其容易受到命令注入的攻击。

以下是fs-git 中存在漏洞的代码片段:

    showRef(): Promise<RefInfo[]> {
        let command=this._buildCommand("show-ref");
        return new Promise((resolve: (value: RefInfo[])=> void, reject: (error: any)=> void)=> {
            child_process.exec(command, { maxBuffer: maxBuffer }, (error, stdout, stderr)=> {
                if (error) {
                    reject(error);
                } else {
                    let list=stdout.toString("utf8").split("\n").filter(line=> !!line);
                    let resultList:RefInfo[]=list.map(str=> {
                        let columns=str.split(" ", 2);
                        返回 {
                            gitDir: this.path、
                            ref: columns[0]、
                            name: columns[1]
                        };
                    });
                    resolve(resultList);

最终,代码还将调用 _buildCommand 函数,其中包含字符串连接和用户提供的数据:

    _buildCommand(...args: string[]): string {
        return `git --git-dir=${this.path} ${args.join(" ") }`;

当攻击者篡改传递给 fs-git 模块的数据以制作利用命令注入漏洞的恶意代码时,攻击就展开了。通过提供精心制作的输入,攻击者能够向系统注入任意命令。这样,攻击者就可以利用运行进程的权限执行未经授权的命令,从而可能危及主机系统。

该漏洞影响深远。攻击者可以执行任意命令,其中可能包括外泄敏感数据、修改文件甚至破坏系统正常运行等操作。对于依赖fs-git的项目和应用程序来说,这个漏洞构成了重大的安全风险。

这个案例充分说明了校验用户所输入的内容和必要的数据清除在防止命令注入漏洞方面的重要性。

所以即使是看似无害的模块,如果不遵循安全编码实践,也会带来严重的安全风险。开发者在处理用户所输入的内容的数据时必须十分谨慎。

安全建议

对于NodeJs项目,我们可以大致从以下几点入手,从而减少命令注入的风险:

  • 使用ORM(对象关系映射)库:使用ORM库可以帮助处理数据库查询,避免手动拼接SQL语句,从而减少SQL注入的风险。

譬如,笔者就在曾经的Egg.js项目中使用过的Sequelize[9] ORM库来执行安全的数据库操作。

  • 校验严谨

对用户所输入的内容进行校验和过滤,以防止恶意输入

  • 遵循安全编码规范

避免直接拼接用户所输入的内容到命令字符串、使用安全的文件路径拼接方法等。确保在代码中进行输入校验和输出转义,并注意处理用户所输入的内容时的边界情况

  • NPM Audit & NSP

使用经过安全审计和更新频繁的第三方库,以减少潜在的安全漏洞。另外还可以使用工具如npm Audit[10]NSP(Node Security Platform)[11]来检查项目依赖的安全性。

回过头看

假设项目中需要使用到exec[12]spawn[13]方法时,如果没有适当的数据清理和校验,用户所输入的内容可能被恶意利用,导致命令注入攻击。

以下是一个简单Demo说明这些类似的场景:

const { exec, spawn }=require('child_process');

// 示例:使用exec执行命令
function executeCommandWithExec(userInput) {
  const command=`ls ${userInput}`; // 拼接用户所输入的内容的命令

  exec(command, (error, stdout, stderr)=> {
    if (error) {
      console.error(`执行命令出错:${error}`);
      return;
    }
    console.log(`命令执行结果:${stdout}`);
  });
}

// 示例:使用spawn执行命令
function executeCommandWithSpawn(userInput) {
  const command='ls';
  const args=[userInput]; // 将用户所输入的内容作为命令行参数

  const child=spawn(command, args);

  child.stdout.on('data', (data)=> {
    console.log(`命令执行结果:${data}`);
  });

  child.stderr.on('data', (data)=> {
    console.error(`执行命令出错:${data}`);
  });
}

// 测试示例
const userInput='; rm -rf /'; // 恶意的用户所输入的内容,尝试删除整个系统

executeCommandWithExec(userInput);
executeCommandWithSpawn(userInput);

在上面的示例中,executeCommandWithExec和executeCommandWithSpawn函数接受用户所输入的内容,并将其用于执行ls命令。

然而,如果恶意用户所输入的内容像; rm -rf /这样的内容,它会将rm -rf /命令添加到ls命令后面,进而导致"删库跑路"的悲剧发生。

为了防止这种攻击,应该对用户所输入的内容进行适当的数据清理和校验。

所以对于以上代码,可以使用安全的执行方法execFilespawn,并将用户所输入的内容作为命令行参数不是直接拼接到命令中:

const { execFile, spawn }=require('child_process');

// 示例:使用execFile执行命令
function executeCommandWithExecFile(userInput) {
  const command='ls';
  const args=[userInput]; // 将用户所输入的内容作为命令行参数

  execFile(command, args, (error, stdout, stderr)=> {
    if (error) {
      console.error(`执行命令出错:${error}`);
      return;
    }
    console.log(`命令执行结果:${stdout}`);
  });
}

// 示例:使用spawn执行命令
function executeCommandWithSpawn(userInput) {
  const command='ls';
  const args=[userInput]; // 将用户所输入的内容作为命令行参数

  const child=spawn(command, args);

  child.stdout.on('data', (data)=> {
    console.log(`命令执行结果:${data}`);
  });

  child.stderr.on('data', (data)=> {
    console.error(`执行命令出错:${data}`);
  });
}

// 测试示例
const userInput='; rm -rf /'; // 恶意的用户所输入的内容,尝试删除整个系统

executeCommandWithExecFile(userInput);
executeCommandWithSpawn(userInput);

在上面的代码实现中,executeCommandWithExecFile函数使用了execFile[14]方法来执行命令,而executeCommandWithSpawn函数保持不变,仍然使用spawn方法执行命令。

使用execFile方法可以避免将用户所输入的内容直接拼接到命令中,这样可以在一定程度上减少命令注入攻击的风险。

总结

记住,无论采用哪种方案,主体思想都应该是谨慎处理用户所输入的内容,并进行严谨的校验,以确保代码的安全性。


[1]

unsplash.com: https://unsplash.com/

[2]

CVE-2018-16487: https://nvd.nist.gov/vuln/detail/CVE-2018-16487

[3]

CVE-2018-17346: https://nvd.nist.gov/vuln/detail/CVE-2018-17346

[4]

CVE-2018-12424: https://nvd.nist.gov/vuln/detail/CVE-2018-12424

[5]

Command Injection: https://owasp.org/www-community/attacks/Command_Injection

[6]

find-exec: https://github.com/shime/find-exec/commit/74fb108097c229b03d6dba4cce81e36aa364b51c

[7]

Securing Your Node.js Apps by Analyzing Real-World Command Injection Examples: https://www.nodejs-security.com/blog/securing-your-nodejs-apps-by-analyzing-real-world-command-injection-examples

[8]

CVE-2017-1000451: https://github.com/advisories/GHSA-wp3j-gv53-4pg8

[9]

sequelize ORM: https://sequelize.org/

[10]

npm audit: https://docs.npmjs.com/cli/v8/commands/npm-audit/

[11]

NSP: https://github.com/nodesecurity/nsp

[12]

exec: https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback

[13]

spawn: https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options

[14]

execFile: https://nodejs.org/docs/latest/api/child_process.html#child_processexecfilefile-args-options-callback


作者:Rien.

来源:微信公众号:奇舞精选

出处:https://mp.weixin.qq.com/s/ElsRQhNTI-Y3qGeVzDzvCA