指在分析源代码的时候并不真正执行代码。手动静态代码分析是指对源代码逐行进行复查以发现潜在的漏洞,对于大型系统,逐行复查并不现实,因为可通过脚本或者代码对其进行检查。
指在代码运行过程中对其进行分析。
代码复查的目标:定位并分析可能隐含程序安全问题的代码段
受感染数据:指从不可信数据来源收到的数据,如果将受感染的数据复制给内部变量,内部变量同样会受到感染;使用经过验证的没有受到感染的程序或输入验证函数可对受感染的数据进行净化。
渗入点:在程序易受攻击的位置,受感染的数据会引发潜在的安全问题,这些易受攻击的位置称为渗入点。
在SQL注入漏洞的代码审计中,我们的渗入点是用于执行涉及数据库的SQL语句。如果无法确保在将从渗入源(产生受感染数据的途径,比如Web表单、cookie、输入参数等)收到的值传递给SQL查询(该查询在数据库服务器上执行)之前已经对其进行验证,那么就会引发SQL注入漏洞。
示例1:
这段代码直接将用户输入的param变量传递给了动态构造的SQL语句,并且该语句未经验证就被执行。
$result = mysql_query("SELECT * FROM table WHERE column = '$_GET["param"]'");
示例2:
这段代码无法准确确定是否存在SQL注入,因为我们并不知道param变量在传递给参数之前是否已经经过验证,这种情况,我们就需要跟踪变量param,识别应用的渗入点并搜索源代码找到param变量赋值的位置和方式。
$result = mysql_query("SELECT * FROM table WHERE column = '$_param' ")
如果我们在跟踪变量代码中发现如下代码,就可以判定存在SQL注入漏洞:
$param = $_GET["param"];
$result = mysql_query("SELECT * FROM table WHERE field = '$param' ");
上述代码存在SQL注入,是因为它直接将易受感染的变量($param)
传递给了动态构造的SQL语句(渗入点)并被执行。如果跟踪发现存在下面三行代码,那么同样可以判断存在SQL注入漏洞(仅仅对长度进行了限制),如下,我们需要跟踪$limit
变量以判断存在多大的注入空间:
$param = $_GET["param"];
if (strlen($param)< $limit){error_handler("param exceeds max length!")}
$result = mysql_query("SELECT * FROM table WHERE field = '$param' ")
在跟踪过程中发现如下两行代码,利用mysql_real_escape_string()函数对param进行验证:
$param = mysql_real_escape_string($param);
$result = mysql_query("SELECT * FROM table WHERE field = '$param' ")
在复查源代码寻找SQL注入时,我们需要定位所有的依赖关系并跟踪数据流。
在这个过程中:
1、根据用户控制的输入(可能已被潜在的感染)来识别负责构建并执行SQL语句(渗入点)的函数;
2、然后识别用户控制数据的输入点,用户控制数据将被传递给这些函数(渗入源);
3、最后通过应用程序的执行流程来跟踪用户控制数据,以便弄清楚数据在到达渗入点时是否已被感染;
4、然后就可以确定是否存在漏洞以及漏洞被利用的可行性。
//在PHP中执行动态构造的SQL语句
mysql_query("SELECT * FROM table WHERE filed = '$_GET["input"]' ");
//在C#中执行动态构造的SQL语句
SqlCommand command = new SqlCommand("SELECT * FROM table WHERE filed = ' " +request.getParameter("input") + " ' ",connection);
//在JAVA中执行动态构造的SQL语句
ResultSet rs = s.executeQuery("SELECT * FROM table WHERE filed = ' " + request.getParameter("input") + " ' ");
//@input变量直接来自用户输入并于SQL字符串相连,该SQL字符串(@sql)作为参数传递给EXEC函数并执行。
//MS SQL SERVER中易受攻击的存储过程
CREATE PROCEDURE SP_SroredProducedure @input varchar(400) = NULL AS DECLARE @sql nvarchar(4000)
SELECT @sql = 'SELECT filed FROM table WHERE filed =' ' ' + @input + ' ' ' '
EXEC (@sql)
//input变量直接来自用户输入并与SQL字符串(@sql)相连,该SQL字符串作为参数传递给EXECUTE函数并被执行
//MySQL中易受攻击的存储过程
CREATE PROCEDURE SP_StoredProcedure (input varchar(400))
BEGIN
SET @param = input;
SET @sql = concat('SELECT field FROM table WHERE field=',@param);
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
End
//中,input变量直接来自用户输入并与SQL字符串(@sql)相连,该SQL字符串作为参数传递给EXECUTE函数并被执行
//Oracle中易受攻击的存储过程
CREATE OR REPLACE PROCEDURE SP_StoredProcedure (input IN VARCHAR2) AS
sql VARCHAR2;
BEGIN
sql := 'SELECT field FROM table WHERE field = ''' || input || '''';
EXECUTE IMMEDIATE sql;
END;
//使用HTML表单获取用户输入
<form name="simple_form" method="get" action = "process_input.php">
<input type="text" name="foo">
<input type="text" name="bar">
<input type="submit" value="submit">
form>
在HTML中,可以为表单指定两种不同的提交方法:GET和POST,可以在FORM元素内部使用METHOD属性来进行指定。GET和POST方法的主要差别在对表单数据的编码方法上。
GET:
Web浏览器将表单的数据编码在URL中;
URL最大长度为2048个字符,此方法传递的参数大小限制。
地址栏:http://www.xxx.com/process_ input.php?foo=input&bar=input”
问号(?)后面的内容称为查询字符串。查询字符串保存了通过表单提交的用户输入(也可以在URL中手动提交)。在查询字符串中,使用和号(&)或分号(;)分隔参数,使用等号(=)分隔参数的名称和值。
POST:
Web浏览器将表单的数据编码在消息体中。
POST方法对大小没有限制。
地址栏:“http://www.xxx.com/process_input.php
当Web服务器使用PHP处理HTTP请求时,PHP将HTTP请求中提交的信息转换成预定义的变量。
PHP开发人员可使用下列函数处理用户输入:
1、$_GET:一个关联数组,用于收集来自 method="get" 的表单中的值。
$variable = $_GET['name'];
2、$_POST:一个关联数组,存放通过HTTP POST方法传递的变量。
$variable = $_POST['name'];
3、$_REQUEST:一个关联数组,包含$_GET、$_ POST 和$_COOKIE 的内容。
$variable = $_REQUEST['name'];
4、$_COOKIE:一个关联数组,存放通过HTTP cookie传递给当前脚本的变量。
$variable = $_COOKIE['name']
5、$_SERVER:服务器及执行环境的信息。是一个包含了诸如头信息、路径、以及脚本位置等等信息的数组。
$variable = $_SERVER['name'];
在JAVA中,可以使用请求对象获取在HTTP请求过程中客户端发送给Web服务器的值,请求对象从客户端的Web浏览器获取值,然后通过HTTP请求传递给服务器。请求对象的类名或接口的名称时HttpServletRequest,使用时可以写成javax.servlet.HttpServletRequest。
HttpServletRequest接口定义的处理用户输入的方法:
1、getParameter():返回所请求的给定参数的值。
String string_variable = request.getParameter("name");
2、getQueryString():返回请求的查询字符串。
String string_variable = request.getQueryString();
3、getHeader():返回所请求的头的值。
String string_variable = request.getHeaders("User-Agent");
4、getHeaders():返回指定名字的request header的所有值,其结果是一个枚举对象。
String string_variable = request.getHeaders("User-Agent");
5、getRequestedSessionId():返回客户端指定的Session ID。
String string_variable = request.getRequestedSessionId();
6、getCookies():返回一个cookie对象的数组。
Cookie[] cookie_array = request.getCookies();
7、cookie.getValue():返回所请求的给定的cookie。
String string_variable = Cookei_array.getValue("name");
在C#中,开发人员使用是System.Web名称空间的HttpRequest类,其包含必要的属性和方法,用于处理HTTP请求和浏览器传递的所有信息(包括所有表单、证书和头信息),和CGI(公共网关接口)服务器变量。
HttpRequest类的属性列表:
1、Form:表单提交过程中从客户端传递的所有表单值的集合。
string variable = Request.Form["name"];
2、Headers:客户端在请求中传递的所有头的集合。
string variable = Request.Headers["name"];
3、Params:获取 QueryString、Form、Cookies 和 ServerVariables 项的组合集合。
string variable = Request.Params["name"];
4、QueryString:获取 HTTP 查询字符串变量集合。
string variable = Request.QueryString["name"];
5、ServerVariable:当前请求的所有Web服务器变量的集合。
string variable = Request.ServerVariables["name"];
6、URL:获取当前请求的URl的信息。
Uri object_variable = Request.Url;
string variable = object_variable.Query;
7、UserAgent:获取所提供的客户端浏览器的原始用户代理字符串。
string variable = Request.UserAgent;
8、UserHostAddress:获取远程客户端的IP主机名称。
string variable = Request.UserHostAddress;
9、UserHostName:获取远程客户端你的DNS名称。
string variable = Request.UserHostName;
10、HttpCookieCollection:客户端在当前请求中传递的所有 cookie 的集合。
HttpCookieCollection variable = Request.Cookies;
PHP支持的数据库厂商:http://www.php.net/manual/en/refs.database.vendors.php
PHP支持的数据库(Mysql、SQL Server、PostgreSQL、Oracle)的相关函数。
1、mssql_query():向当前使用的数据库发送一个查询。
$result = mssql_query($sql)
2、mysql_query():向当前使用的数据库发送一个查询。
$result = mysql+query($sql)
3、oci_parse():在语句执行之前对其进行解析(在oci_execute()/ociexecute()之前)。
$stmt = oci_parse($connection,$sql);
ociexecute($stmt);
4、ora_parse():语句在执行之前进行解析(在ora_exec()之前)。
if(!ora_parse($cursor,$sql)){exit;}
else{ora_exec($curosr);}
5、mssql_bind():向存储过程添加一个参数(在mssql_execute()之前。
mssql_bind(&stmt, '@param', $variable, SQLVARCHAR, false, false, 100);
$result = mssql_execute($stmt);
6、odbc_prepare():准备一条执行语句(在odbc_execute()之前)。
$stmt = odbc_prepare($db, $sql);
$result = odbc_execute($stmt);
7、odbc_execute():执行一条SQL语句。
8、odbc_exec():准备并执行一条SQL语句。
$result = odbc_exec($db, $sql);
9、pg_query():执行一个查询。
$result = pg_query($conn, $sql);
10、pg_send_query():发送一个异步查询。
pg_send_query($conn, $sql);
11、pg_send_query_params():向服务器提交一个命令并分离参数,无需等待完成。
pg_send_query_params($conn, $sql, $params)
12、pg_query_params():向服务器提交一个命令并等待结果。
pg_query_params($conn, $sql, $params)
13、pg_send_prepare():发送一个请求以创建一个具有执行参数的预备语句,无须等待完成。
pg_send_prepare($conn, "my_query", 'SELECT * FROM table WHERE field = $1');
pg_send_execute($conn, "my_query", $var);
14、pg_prepare():发送一个请求以创建一条具有执行参数的预备语句并等待完成。
pg_prepare($conn, "my_query", 'SELECT * FROM table WHERE field = $1');
pg_execute($conn, "my_query", $var);
15、pg_select():根据指定的assoc_array选择记录。
$result = pg_select($conn, $table_name, $assoc_array)
16、pg_update():用数据更新与指定条件匹配的记录。
pg_update($conn, $arr_update, $arr_where);
17、pg_insert():将assoc_array的值插入到指定的表中。
pg_insert($conn, $table_name, $assoc_array)
18、pg_delete():根据assoc_array中指定的键和值删除表中的记录。
pg_delete($conn, $table_name, $assoc_array)
Java提供了java.sql包,为数据库连接提供了JDBC(java数据库连接)API。
Java支持的数据库厂商:http://java.sun.com/products/jdbc/driverdesc.html
Java支持的数据库(Mysql、SQL Server、PostgreSQL、Oracle等)的相关函数。
1、createStatement():创建一个语句对象以便向数据库发送SQL语句。
statement = connection.createStatement();
2、prepareStatement():创建一条预编译的SQL语句并将其保存到对象中。
PrepareStatement sql = con.prepareStatement(sql);
3、executeQuery():执行给定的SQL语句,从指定的表中获取数据。
result = statement.executeQuery(sql);
4、executeUpdate():执行一条SQL语句,该语句可能是一条条任何返回值的INSETRT、UPDATE、DELETE语句。
result = statement.executeUpdate(sql);
5、execute:执行给定的SQL语句,从指定的表中获取数据。
result = statement.execute(sql);
6、addBatch:将指定的SQL命令添加到当前命令列表中。
statement.addBatch(sql);
statement.addBatch(more_sql);
C#支持的数据库(Mysql、SQL Server、PostgreSQL、Oracle等)的相关函数。
1、SqlCommand():用于构造或发送 SQL 语句或存储过程。。
SqlCommand command = new SqlCommand(sql, connection);
2、SqlParameter():用于向 SqlCommand 对象添加参数
SqlCommand command = new SqlCommand(sql, connection);
command.Parameters.Add("@param", SqlDbType.VarChar, 50).Value = input;
3、OleDbCommand()——用于构造或发送 SQL 语句或存储过程
OleDbCommand command = new OleDbCommand(sql,connection);
4、OleDbParameter()——用于向 OleDbCommand 对象添加参数
OleDvCommand command = new OleDbCommand($sql,connection);
command.parameters.Add("@paran", OleDbType.VarChar, 50).Value = input;
5、OracleCommand()——用于构造/发送 SQL 语句或存储过程
oracleCommand command = new OracleCommand(sql,connection);
6、OracleParameter()——用于向 OracleCommand 对象添加参数
OracleCommand command = new OracleCommand(sql,connection);
command.Parameters.Add("@param", OleDbType.VarChar, 50).Value = input;
7、OdbcCommand()——用于构造或发送 SQL 语句
OdbcCommand command = new OdbcCommand(sql,connection);
8、OdbcParameter()——用于向 OdbcCommand 对象添加参数
OdbcCommand command = new OdbcCommand(sql, connection);
command.Parameters.Add("@param", OleDbType.VarChar, 50).Value = input