由AppScan源标记进行的安全扫描,必须在下面的代码中的$this->aURI = explode('/', $_SERVER['SCRIPT_NAME']);
$sRequest = preg_replace('!'.$this->sURI.'(.*)$!i', '$1', $_SERVER['REQUEST_URI']);
if(substr($sRequest, -1)!='/')
$sRequest .= '/';
$sGets = $this->parseUrl($sRequest);
行上验证输入(Validation.Required):
private function parseUrl($sRequest){
$sVars = null;
foreach($this->aRoutingParse AS $k => $v){
if(!is_array($v))
continue;
preg_match_all('!\[(.+?)\]!i', $v[0], $aExpression_);
$sExpression = preg_replace_callback('!\[(.+?)\]!i', function($m) use ($k){
return $this->transformParam($m[1], $k);
}, $v[0]);
if(preg_match_all('!'.$sExpression.'!i', $sRequest, $aExpression__)){
foreach($aExpression__ AS $k_ => $v_){
foreach($v_ AS $kkk => $vvv){
if(!isset($aExpression_[1][$k_-1]))
$aExpression_[1][$k_-1] = null;
if($kkk>0)
$aExpression[] = array($aExpression_[1][$k_-1].'_'.$kkk, $vvv);
else
$aExpression[] = array($aExpression_[1][$k_-1], $vvv);
}
}
unset($aExpression[0]);
$iCount = count($aExpression__[0]);
if($iCount>1){
for($i=0;$i<$iCount;$i++){
if($i>0)
$sVars .= '&'.preg_replace_callback('!\[(.+?)\]!i', '[$1_'.$i.']', $v[1]);
else
$sVars = '&'.$v[1];
}
}else
$sVars = '&'.$v[1];
foreach($aExpression AS $k => $v_){
if(!isset($v['_'.$v_[0]]))
$v['_'.$v_[0]] = null;
if(!is_array($v['_'.$v_[0]]))
$sVars = str_replace('['.$v_[0].']', $v_[1], $sVars);
else {
$this->aRoutingParse = array($v['_'.$v_[0]]);
$sVars = $sVars.$this->parseUrl($v_[1]);
}
}
break;
}
}
return $sVars;
}
我认为这背后的意图是避免SQL注入攻击,但我不确定在那种情况下是否可行。
问题:通过这些JDBC方法可以进行SQL注入攻击吗? JDBC如何在幕后实现这一点?这会是AppScan报告的另一个误报吗?
答案 0 :(得分:3)
我不确定bluemix-app-scan,但我在这里提供了解释。 (这是我基于以下测试和代码粘贴的假设)
我运行了一个测试代码来检查(在 H2 DB 中)
值 testName 字符串:(select 'sqlInjection' from dual)
使用 createStatement
(不安全):
String query = "update TEST_TABLE set TEST_CHAR = " + testName + " where ID = 1";
Statement statement = connection.createStatement();
statement.executeUpdate(query);
输出:数据库中的TEST_CHAR为 sqlInjection 。
使用 ResultSet
createStatement
(H2数据库中的安全):
String query = "select * from TEST_TABLE where ID = 1";
Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet executeQuery = statement.executeQuery(query);
executeQuery.next();
executeQuery.updateString("TEST_CHAR", testName);
executeQuery.updateRow();
输出:令人惊讶的是,数据库中的TEST_CHAR是(选择&#39; sqlInjection&#39;来自双重)。
使用 PreparedStatement
(安全):
String query = "update TEST_TABLE set TEST_CHAR = ? where ID = 1";
PreparedStatement statement = connection.prepareStatement(query);
statement.setString(1, testName);
statement.executeUpdate();
输出预期 - 数据库中的TEST_CHAR为(选择&#39; sqlInjection&#39;来自双重)。
使用 ResultSet
prepareStatement
(H2数据库中的安全):
String query = "select * from TEST_TABLE where ID = 1";
PreparedStatement statement = connection.prepareStatement(query, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
ResultSet uprs = statement.executeQuery();
uprs.next();
uprs.updateString("TEST_CHAR", testName);
uprs.updateRow();
输出预期 - 数据库中的TEST_CHAR为(选择&#39; sqlInjection&#39;来自双重)。
返回问题:
通过这些JDBC方法可以进行SQL注入攻击吗?
可能。这取决于您正在使用的数据库驱动程序
如何?:
SQL注入在我的结果集更新中失败的原因是因为H2数据库在调用PreparedStatement
时内部使用ResultSet.updateRow()
来更新行。
public void updateRow(Value[] current, Value[] updateRow) throws SQLException {
StatementBuilder buff = new StatementBuilder("UPDATE ");
...
buff.append(" SET ");
appendColumnList(buff, true);
...
appendKeyCondition(buff);
PreparedStatement prep = conn.prepareStatement(buff.toString());
...
for (int i = 0; i < columnCount; i++) {
...
v.set(prep, j++);
}
setKey(prep, j, current);
int count = prep.executeUpdate();
...
}
我不确定java中的所有数据库驱动程序是否使用updateRow()
实现了preparedStatement
方法。但是很明显,这是留给司机的,如果bluemix建议你在这里添加验证,我建议你遵循:)
JDBC如何在幕后实现这一点?
好吧,如上所示,这是特定于驱动程序的。但是,PreparedStatement
对here的处理方式有一个很好的解释。
这会是AppScan报告的另一个误报吗?
我不认为这是误报(但在像H2 DB这样的情况下),但你永远不会知道所有数据库驱动程序是否安全地实现了这一点。
编辑 -
甚至PostgreSQL和MySQL都使用PreparedStatement来处理这个问题。
public synchronized void updateRow() throws SQLException
{
...
updateStatement = ((java.sql.Connection) connection).prepareStatement(updateSQL.toString());
...
updateStatement.executeUpdate();
...
}