mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
improved wallfilter. support update column check.
This commit is contained in:
@@ -490,4 +490,22 @@ public class SQLBinaryOpExpr extends SQLExprImpl implements SQLReplaceable, Seri
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean conditionContainsColumn(String column) {
|
||||
if (left == null || right == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (left instanceof SQLIdentifierExpr) {
|
||||
if (((SQLIdentifierExpr) left).nameEquals(column)) {
|
||||
return true;
|
||||
}
|
||||
} else if (right instanceof SQLIdentifierExpr) {
|
||||
if (((SQLIdentifierExpr) right).nameEquals(column)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,4 +178,8 @@ public class SQLIdentifierExpr extends SQLExprImpl implements SQLName {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean nameEquals(String name) {
|
||||
return SQLUtils.nameEquals(this.name, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,4 +199,8 @@ public class SQLPropertyExpr extends SQLExprImpl implements SQLName {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean nameEquals(String name) {
|
||||
return SQLUtils.nameEquals(this.name, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ package com.alibaba.druid.sql.ast.statement;
|
||||
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
import com.alibaba.druid.sql.ast.SQLObjectImpl;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
|
||||
import com.alibaba.druid.sql.visitor.SQLASTVisitor;
|
||||
|
||||
public class SQLUpdateSetItem extends SQLObjectImpl {
|
||||
@@ -60,4 +62,12 @@ public class SQLUpdateSetItem extends SQLObjectImpl {
|
||||
visitor.endVisit(this);
|
||||
}
|
||||
|
||||
public boolean columnMatch(String column) {
|
||||
if (this.column instanceof SQLIdentifierExpr) {
|
||||
return ((SQLIdentifierExpr) this.column).nameEquals(column);
|
||||
} else if (this.column instanceof SQLPropertyExpr) {
|
||||
((SQLPropertyExpr) this.column).nameEquals(column);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,8 +58,14 @@ public class SQLUpdateStatement extends SQLStatementImpl implements SQLReplaceab
|
||||
|
||||
public SQLName getTableName() {
|
||||
if (tableSource instanceof SQLExprTableSource) {
|
||||
SQLExprTableSource exprTableSource = (SQLExprTableSource) tableSource;
|
||||
return (SQLName) exprTableSource.getExpr();
|
||||
return ((SQLExprTableSource) tableSource).getName();
|
||||
}
|
||||
|
||||
if (tableSource instanceof SQLJoinTableSource) {
|
||||
SQLTableSource left = ((SQLJoinTableSource) tableSource).getLeft();
|
||||
if (left instanceof SQLExprTableSource) {
|
||||
return ((SQLExprTableSource) left).getName();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1395,8 +1395,9 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter {
|
||||
|
||||
setMode(x, Mode.Update);
|
||||
|
||||
SQLName identName = x.getTableName();
|
||||
if (identName != null) {
|
||||
SQLTableSource tableSource = x.getTableSource();
|
||||
if (tableSource instanceof SQLExprTableSource) {
|
||||
SQLName identName = ((SQLExprTableSource) tableSource).getName();
|
||||
String ident = identName.toString();
|
||||
setCurrentTable(ident);
|
||||
|
||||
@@ -1406,7 +1407,7 @@ public class SchemaStatVisitor extends SQLASTVisitorAdapter {
|
||||
Map<String, String> aliasMap = getAliasMap();
|
||||
putAliasMap(aliasMap, ident, ident);
|
||||
} else {
|
||||
x.getTableSource().accept(this);
|
||||
tableSource.accept(this);
|
||||
}
|
||||
|
||||
accept(x.getFrom());
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.util.Map;
|
||||
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||
|
||||
public class WallCheckResult {
|
||||
|
||||
private final List<SQLStatement> statementList;
|
||||
private final Map<String, WallSqlTableStat> tableStats;
|
||||
|
||||
@@ -36,6 +35,8 @@ public class WallCheckResult {
|
||||
|
||||
private String sql;
|
||||
|
||||
private List<WallUpdateCheckItem> updateCheckItems;
|
||||
|
||||
public WallCheckResult(){
|
||||
this(null);
|
||||
}
|
||||
@@ -103,4 +104,12 @@ public class WallCheckResult {
|
||||
public WallSqlStat getSqlStat() {
|
||||
return sqlStat;
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getUpdateCheckItems() {
|
||||
return updateCheckItems;
|
||||
}
|
||||
|
||||
public void setUpdateCheckItems(List<WallUpdateCheckItem> updateCheckItems) {
|
||||
this.updateCheckItems = updateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
*/
|
||||
package com.alibaba.druid.wall;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.wall.spi.WallVisitorUtils;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
|
||||
import static com.alibaba.druid.util.Utils.getBoolean;
|
||||
@@ -129,6 +129,9 @@ public class WallConfig implements WallConfigMBean {
|
||||
|
||||
private int selectLimit = -1;
|
||||
|
||||
protected Map<String, Set<String>> updateCheckColumns = new HashMap<String, Set<String>>();
|
||||
protected WallUpdateCheckHandler updateCheckHandler;
|
||||
|
||||
public WallConfig(){
|
||||
this.configFromProperties(System.getProperties());
|
||||
}
|
||||
@@ -852,5 +855,53 @@ public class WallConfig implements WallConfigMBean {
|
||||
this.setSelectLimit(propertyValue);
|
||||
}
|
||||
}
|
||||
{
|
||||
String propertyValue = properties.getProperty("druid.wall.updateCheckColumns");
|
||||
if (propertyValue != null) {
|
||||
String[] items = propertyValue.split(",");
|
||||
for (String item : items) {
|
||||
addUpdateCheckCoumns(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addUpdateCheckCoumns(String columnInfo) {
|
||||
String[] items = columnInfo.split("\\.");
|
||||
if (items.length != 2) {
|
||||
return;
|
||||
}
|
||||
String table = SQLUtils.normalize(items[0]).toLowerCase();
|
||||
String column = SQLUtils.normalize(items[1]).toLowerCase();
|
||||
Set<String> columns = this.updateCheckColumns.get(table);
|
||||
if (columns == null) {
|
||||
columns = new LinkedHashSet<String>();
|
||||
updateCheckColumns.put(table, columns);
|
||||
}
|
||||
columns.add(column);
|
||||
}
|
||||
|
||||
public boolean isUpdateCheckTable(String tableName) {
|
||||
if (updateCheckColumns.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
String tableNameLower = SQLUtils.normalize(tableName).toLowerCase();
|
||||
return updateCheckColumns.containsKey(tableNameLower);
|
||||
}
|
||||
|
||||
public Set<String> getUpdateCheckTable(String tableName) {
|
||||
if (updateCheckColumns.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
String tableNameLower = SQLUtils.normalize(tableName).toLowerCase();
|
||||
return updateCheckColumns.get(tableNameLower);
|
||||
}
|
||||
|
||||
public WallUpdateCheckHandler getUpdateCheckHandler() {
|
||||
return updateCheckHandler;
|
||||
}
|
||||
|
||||
public void setUpdateCheckHandler(WallUpdateCheckHandler updateCheckHandler) {
|
||||
this.updateCheckHandler = updateCheckHandler;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.alibaba.druid.wall;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WallContext {
|
||||
@@ -33,6 +34,8 @@ public class WallContext {
|
||||
private int deleteNoneConditionWarnings = 0;
|
||||
private int likeNumberWarnings = 0;
|
||||
|
||||
private List<WallUpdateCheckItem> wallUpdateCheckItems;
|
||||
|
||||
public WallContext(String dbType){
|
||||
this.dbType = dbType;
|
||||
}
|
||||
@@ -186,4 +189,11 @@ public class WallContext {
|
||||
this.deleteNoneConditionWarnings++;
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getWallUpdateCheckItems() {
|
||||
return wallUpdateCheckItems;
|
||||
}
|
||||
|
||||
public void setWallUpdateCheckItems(List<WallUpdateCheckItem> wallUpdateCheckItems) {
|
||||
this.wallUpdateCheckItems = wallUpdateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,10 @@ package com.alibaba.druid.wall;
|
||||
|
||||
import com.alibaba.druid.filter.FilterAdapter;
|
||||
import com.alibaba.druid.filter.FilterChain;
|
||||
import com.alibaba.druid.proxy.jdbc.CallableStatementProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.ConnectionProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.DataSourceProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.PreparedStatementProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.ResultSetMetaDataProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.ResultSetProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.StatementProxy;
|
||||
import com.alibaba.druid.proxy.jdbc.*;
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLValuableExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLVariantRefExpr;
|
||||
import com.alibaba.druid.support.logging.Log;
|
||||
import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
@@ -79,6 +76,7 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
private volatile boolean throwException = true;
|
||||
|
||||
public final static String ATTR_SQL_STAT = "wall.sqlStat";
|
||||
public final static String ATTR_UPDATE_CHECK_ITEMS = "wall.updateCheckItems";
|
||||
|
||||
public WallFilter(){
|
||||
configFromProperties(System.getProperties());
|
||||
@@ -248,9 +246,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection, String sql)
|
||||
throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
PreparedStatementProxy stmt = chain.connection_prepareStatement(connection, sql);
|
||||
setSqlStatAttribute(stmt);
|
||||
return stmt;
|
||||
@@ -263,9 +263,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection,
|
||||
String sql, int autoGeneratedKeys) throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
PreparedStatementProxy stmt = chain.connection_prepareStatement(connection, sql, autoGeneratedKeys);
|
||||
setSqlStatAttribute(stmt);
|
||||
return stmt;
|
||||
@@ -279,9 +281,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
String sql, int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
PreparedStatementProxy stmt = chain.connection_prepareStatement(connection, sql, resultSetType,
|
||||
resultSetConcurrency);
|
||||
setSqlStatAttribute(stmt);
|
||||
@@ -296,9 +300,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
String sql, int resultSetType, int resultSetConcurrency,
|
||||
int resultSetHoldability) throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
PreparedStatementProxy stmt = chain.connection_prepareStatement(connection, sql, resultSetType,
|
||||
resultSetConcurrency, resultSetHoldability);
|
||||
setSqlStatAttribute(stmt);
|
||||
@@ -312,9 +318,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection,
|
||||
String sql, int[] columnIndexes) throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
PreparedStatementProxy stmt = chain.connection_prepareStatement(connection, sql, columnIndexes);
|
||||
setSqlStatAttribute(stmt);
|
||||
return stmt;
|
||||
@@ -327,9 +335,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
public PreparedStatementProxy connection_prepareStatement(FilterChain chain, ConnectionProxy connection,
|
||||
String sql, String[] columnNames) throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
PreparedStatementProxy stmt = chain.connection_prepareStatement(connection, sql, columnNames);
|
||||
setSqlStatAttribute(stmt);
|
||||
return stmt;
|
||||
@@ -342,9 +352,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
public CallableStatementProxy connection_prepareCall(FilterChain chain, ConnectionProxy connection, String sql)
|
||||
throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
CallableStatementProxy stmt = chain.connection_prepareCall(connection, sql);
|
||||
setSqlStatAttribute(stmt);
|
||||
return stmt;
|
||||
@@ -358,9 +370,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
int resultSetType, int resultSetConcurrency)
|
||||
throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
CallableStatementProxy stmt = chain.connection_prepareCall(connection, sql, resultSetType,
|
||||
resultSetConcurrency);
|
||||
setSqlStatAttribute(stmt);
|
||||
@@ -375,9 +389,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
int resultSetType, int resultSetConcurrency,
|
||||
int resultSetHoldability) throws SQLException {
|
||||
String dbType = connection.getDirectDataSource().getDbType();
|
||||
WallContext.create(dbType);
|
||||
WallContext context = WallContext.create(dbType);
|
||||
try {
|
||||
sql = check(sql);
|
||||
WallCheckResult result = checkInternal(sql);
|
||||
context.setWallUpdateCheckItems(result.getUpdateCheckItems());
|
||||
sql = result.getSql();
|
||||
CallableStatementProxy stmt = chain.connection_prepareCall(connection, sql, resultSetType,
|
||||
resultSetConcurrency, resultSetHoldability);
|
||||
setSqlStatAttribute(stmt);
|
||||
@@ -598,6 +614,8 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
@Override
|
||||
public boolean preparedStatement_execute(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
|
||||
try {
|
||||
wallUpdateCheck(statement);
|
||||
|
||||
boolean firstResult = chain.preparedStatement_execute(statement);
|
||||
|
||||
if (!firstResult) {
|
||||
@@ -631,6 +649,8 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
@Override
|
||||
public int preparedStatement_executeUpdate(FilterChain chain, PreparedStatementProxy statement) throws SQLException {
|
||||
try {
|
||||
wallUpdateCheck(statement);
|
||||
|
||||
int updateCount = chain.preparedStatement_executeUpdate(statement);
|
||||
WallSqlStat sqlStat = (WallSqlStat) statement.getAttribute(ATTR_SQL_STAT);
|
||||
if (sqlStat != null) {
|
||||
@@ -643,6 +663,49 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
}
|
||||
}
|
||||
|
||||
private void wallUpdateCheck(PreparedStatementProxy statement) throws SQLException {
|
||||
Map<Integer, JdbcParameter> parameterMap = statement.getParameters();
|
||||
List<WallUpdateCheckItem> wallUpdateCheckItems = (List<WallUpdateCheckItem>) statement.getAttribute(ATTR_UPDATE_CHECK_ITEMS);
|
||||
if (wallUpdateCheckItems != null) {
|
||||
for (WallUpdateCheckItem item : wallUpdateCheckItems) {
|
||||
Object setValue;
|
||||
if (item.value instanceof SQLValuableExpr) {
|
||||
setValue = ((SQLValuableExpr) item.value).getValue();
|
||||
} else {
|
||||
int index = ((SQLVariantRefExpr) item.value).getIndex();
|
||||
JdbcParameter parameter = parameterMap.get(index);
|
||||
if (parameter != null) {
|
||||
setValue = parameter.getValue();
|
||||
} else {
|
||||
setValue = null;
|
||||
}
|
||||
}
|
||||
|
||||
List<Object> filtersValues = new ArrayList<Object>(item.filterValues.size());
|
||||
for (SQLExpr filterValueExpr : item.filterValues) {
|
||||
Object filterValue;
|
||||
if (filterValueExpr instanceof SQLValuableExpr) {
|
||||
filterValue = ((SQLValuableExpr) filterValueExpr).getValue();
|
||||
} else {
|
||||
int index = ((SQLVariantRefExpr) filterValueExpr).getIndex();
|
||||
JdbcParameter parameter = parameterMap.get(index);
|
||||
if (parameter != null) {
|
||||
filterValue = parameter.getValue();
|
||||
} else {
|
||||
filterValue = null;
|
||||
}
|
||||
}
|
||||
filtersValues.add(filterValue);
|
||||
}
|
||||
|
||||
boolean valid = config.updateCheckHandler.check(item.tableName, item.columnName, setValue, filtersValues);
|
||||
if (!valid) {
|
||||
throw new SQLException("wall update check failed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultSetProxy statement_getResultSet(FilterChain chain, StatementProxy statement) throws SQLException {
|
||||
ResultSetProxy resultSetProxy = chain.statement_getResultSet(statement);
|
||||
@@ -669,6 +732,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
}
|
||||
|
||||
stmt.putAttribute(ATTR_SQL_STAT, sqlStat);
|
||||
|
||||
List<WallUpdateCheckItem> wallUpdateCheckItems = context.getWallUpdateCheckItems();
|
||||
if (wallUpdateCheckItems != null) {
|
||||
stmt.putAttribute(ATTR_UPDATE_CHECK_ITEMS, wallUpdateCheckItems);
|
||||
}
|
||||
}
|
||||
|
||||
public void statExecuteUpdate(int updateCount) {
|
||||
@@ -709,6 +777,11 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
}
|
||||
|
||||
public String check(String sql) throws SQLException {
|
||||
return checkInternal(sql)
|
||||
.getSql();
|
||||
}
|
||||
|
||||
private WallCheckResult checkInternal(String sql) throws SQLException {
|
||||
WallCheckResult checkResult = provider.check(sql);
|
||||
List<Violation> violations = checkResult.getViolations();
|
||||
|
||||
@@ -722,14 +795,14 @@ public class WallFilter extends FilterAdapter implements WallFilterMBean {
|
||||
if (violations.get(0) instanceof SyntaxErrorViolation) {
|
||||
SyntaxErrorViolation violation = (SyntaxErrorViolation) violations.get(0);
|
||||
throw new SQLException("sql injection violation, " + firstViolation.getMessage() + " : " + sql,
|
||||
violation.getException());
|
||||
violation.getException());
|
||||
} else {
|
||||
throw new SQLException("sql injection violation, " + firstViolation.getMessage() + " : " + sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return checkResult.getSql();
|
||||
return checkResult;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -704,6 +704,8 @@ public abstract class WallProvider {
|
||||
}
|
||||
result.setSql(resultSql);
|
||||
|
||||
result.setUpdateCheckItems(visitor.getUpdateCheckItems());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 1999-2017 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.druid.wall;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by wenshao on 13/08/2017.
|
||||
*/
|
||||
public interface WallUpdateCheckHandler {
|
||||
boolean check(String table, String column, Object setValue, List<Object> filterValues);
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 1999-2017 Alibaba Group Holding Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.alibaba.druid.wall;
|
||||
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Created by wenshao on 14/08/2017.
|
||||
*/
|
||||
public class WallUpdateCheckItem {
|
||||
public final String tableName;
|
||||
public final String columnName;
|
||||
public final SQLExpr value;
|
||||
public final List<SQLExpr> filterValues;
|
||||
|
||||
public WallUpdateCheckItem(String tableName, String columnName, SQLExpr value, List<SQLExpr> filterValues) {
|
||||
this.tableName = tableName;
|
||||
this.columnName = columnName;
|
||||
this.value = value;
|
||||
this.filterValues = filterValues;
|
||||
}
|
||||
}
|
||||
@@ -43,4 +43,8 @@ public interface WallVisitor extends SQLASTVisitor {
|
||||
boolean isSqlEndOfComment();
|
||||
|
||||
void setSqlEndOfComment(boolean sqlEndOfComment);
|
||||
|
||||
void addWallUpdateCheckItem(WallUpdateCheckItem item);
|
||||
|
||||
List<WallUpdateCheckItem> getUpdateCheckItems();
|
||||
}
|
||||
|
||||
@@ -28,10 +28,7 @@ import com.alibaba.druid.sql.ast.statement.*;
|
||||
import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;
|
||||
import com.alibaba.druid.sql.dialect.db2.visitor.DB2ASTVisitorAdapter;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.wall.Violation;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.WallVisitor;
|
||||
import com.alibaba.druid.wall.*;
|
||||
import com.alibaba.druid.wall.violation.ErrorCode;
|
||||
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
|
||||
|
||||
@@ -45,6 +42,7 @@ public class DB2WallVisitor extends DB2ASTVisitorAdapter implements WallVisitor
|
||||
private final List<Violation> violations = new ArrayList<Violation>();
|
||||
private boolean sqlModified = false;
|
||||
private boolean sqlEndOfComment = false;
|
||||
private List<WallUpdateCheckItem> updateCheckItems;
|
||||
|
||||
public DB2WallVisitor(WallProvider provider){
|
||||
this.config = provider.getConfig();
|
||||
@@ -279,4 +277,15 @@ public class DB2WallVisitor extends DB2ASTVisitorAdapter implements WallVisitor
|
||||
public void setSqlEndOfComment(boolean sqlEndOfComment) {
|
||||
this.sqlEndOfComment = sqlEndOfComment;
|
||||
}
|
||||
|
||||
public void addWallUpdateCheckItem(WallUpdateCheckItem item) {
|
||||
if (updateCheckItems == null) {
|
||||
updateCheckItems = new ArrayList<WallUpdateCheckItem>();
|
||||
}
|
||||
updateCheckItems.add(item);
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getUpdateCheckItems() {
|
||||
return updateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,12 +42,7 @@ import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlUpdateStatement;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitor;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlASTVisitorAdapter;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.wall.Violation;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.WallContext;
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.WallSqlTableStat;
|
||||
import com.alibaba.druid.wall.WallVisitor;
|
||||
import com.alibaba.druid.wall.*;
|
||||
import com.alibaba.druid.wall.spi.WallVisitorUtils.WallTopStatementContext;
|
||||
import com.alibaba.druid.wall.violation.ErrorCode;
|
||||
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
|
||||
@@ -59,6 +54,7 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi
|
||||
private final List<Violation> violations = new ArrayList<Violation>();
|
||||
private boolean sqlModified = false;
|
||||
private boolean sqlEndOfComment = false;
|
||||
private List<WallUpdateCheckItem> updateCheckItems;
|
||||
|
||||
public MySqlWallVisitor(WallProvider provider){
|
||||
this.config = provider.getConfig();
|
||||
@@ -449,5 +445,15 @@ public class MySqlWallVisitor extends MySqlASTVisitorAdapter implements WallVisi
|
||||
public void setSqlEndOfComment(boolean sqlEndOfComment) {
|
||||
this.sqlEndOfComment = sqlEndOfComment;
|
||||
}
|
||||
|
||||
|
||||
public void addWallUpdateCheckItem(WallUpdateCheckItem item) {
|
||||
if (updateCheckItems == null) {
|
||||
updateCheckItems = new ArrayList<WallUpdateCheckItem>();
|
||||
}
|
||||
updateCheckItems.add(item);
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getUpdateCheckItems() {
|
||||
return updateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,10 +38,7 @@ import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectTableReference;
|
||||
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleUpdateStatement;
|
||||
import com.alibaba.druid.sql.dialect.oracle.visitor.OracleASTVisitorAdapter;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.wall.Violation;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.WallVisitor;
|
||||
import com.alibaba.druid.wall.*;
|
||||
import com.alibaba.druid.wall.violation.ErrorCode;
|
||||
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
|
||||
|
||||
@@ -52,6 +49,7 @@ public class OracleWallVisitor extends OracleASTVisitorAdapter implements WallVi
|
||||
private final List<Violation> violations = new ArrayList<Violation>();
|
||||
private boolean sqlModified = false;
|
||||
private boolean sqlEndOfComment = false;
|
||||
private List<WallUpdateCheckItem> updateCheckItems;
|
||||
|
||||
public OracleWallVisitor(WallProvider provider){
|
||||
this.config = provider.getConfig();
|
||||
@@ -345,4 +343,15 @@ public class OracleWallVisitor extends OracleASTVisitorAdapter implements WallVi
|
||||
public void setSqlEndOfComment(boolean sqlEndOfComment) {
|
||||
this.sqlEndOfComment = sqlEndOfComment;
|
||||
}
|
||||
|
||||
public void addWallUpdateCheckItem(WallUpdateCheckItem item) {
|
||||
if (updateCheckItems == null) {
|
||||
updateCheckItems = new ArrayList<WallUpdateCheckItem>();
|
||||
}
|
||||
updateCheckItems.add(item);
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getUpdateCheckItems() {
|
||||
return updateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,10 +29,7 @@ import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;
|
||||
import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectStatement;
|
||||
import com.alibaba.druid.sql.dialect.postgresql.visitor.PGASTVisitorAdapter;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.wall.Violation;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.WallVisitor;
|
||||
import com.alibaba.druid.wall.*;
|
||||
import com.alibaba.druid.wall.violation.ErrorCode;
|
||||
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
|
||||
|
||||
@@ -46,6 +43,7 @@ public class PGWallVisitor extends PGASTVisitorAdapter implements WallVisitor {
|
||||
private final List<Violation> violations = new ArrayList<Violation>();
|
||||
private boolean sqlModified = false;
|
||||
private boolean sqlEndOfComment = false;
|
||||
private List<WallUpdateCheckItem> updateCheckItems;
|
||||
|
||||
public PGWallVisitor(WallProvider provider){
|
||||
this.config = provider.getConfig();
|
||||
@@ -302,4 +300,15 @@ public class PGWallVisitor extends PGASTVisitorAdapter implements WallVisitor {
|
||||
public void setSqlEndOfComment(boolean sqlEndOfComment) {
|
||||
this.sqlEndOfComment = sqlEndOfComment;
|
||||
}
|
||||
|
||||
public void addWallUpdateCheckItem(WallUpdateCheckItem item) {
|
||||
if (updateCheckItems == null) {
|
||||
updateCheckItems = new ArrayList<WallUpdateCheckItem>();
|
||||
}
|
||||
updateCheckItems.add(item);
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getUpdateCheckItems() {
|
||||
return updateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,10 +33,7 @@ import com.alibaba.druid.sql.dialect.sqlserver.ast.stmt.SQLServerInsertStatement
|
||||
import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitor;
|
||||
import com.alibaba.druid.sql.dialect.sqlserver.visitor.SQLServerASTVisitorAdapter;
|
||||
import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.wall.Violation;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.WallVisitor;
|
||||
import com.alibaba.druid.wall.*;
|
||||
import com.alibaba.druid.wall.spi.WallVisitorUtils.WallTopStatementContext;
|
||||
import com.alibaba.druid.wall.violation.ErrorCode;
|
||||
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
|
||||
@@ -51,6 +48,7 @@ public class SQLServerWallVisitor extends SQLServerASTVisitorAdapter implements
|
||||
private final List<Violation> violations = new ArrayList<Violation>();
|
||||
private boolean sqlModified = false;
|
||||
private boolean sqlEndOfComment = false;
|
||||
private List<WallUpdateCheckItem> updateCheckItems;
|
||||
|
||||
public SQLServerWallVisitor(WallProvider provider){
|
||||
this.config = provider.getConfig();
|
||||
@@ -338,4 +336,15 @@ public class SQLServerWallVisitor extends SQLServerASTVisitorAdapter implements
|
||||
public void setSqlEndOfComment(boolean sqlEndOfComment) {
|
||||
this.sqlEndOfComment = sqlEndOfComment;
|
||||
}
|
||||
|
||||
public void addWallUpdateCheckItem(WallUpdateCheckItem item) {
|
||||
if (updateCheckItems == null) {
|
||||
updateCheckItems = new ArrayList<WallUpdateCheckItem>();
|
||||
}
|
||||
updateCheckItems.add(item);
|
||||
}
|
||||
|
||||
public List<WallUpdateCheckItem> getUpdateCheckItems() {
|
||||
return updateCheckItems;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,13 +78,9 @@ import com.alibaba.druid.support.logging.LogFactory;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.alibaba.druid.util.ServletPathMatcher;
|
||||
import com.alibaba.druid.util.StringUtils;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.*;
|
||||
import com.alibaba.druid.wall.WallConfig.TenantCallBack;
|
||||
import com.alibaba.druid.wall.WallConfig.TenantCallBack.StatementType;
|
||||
import com.alibaba.druid.wall.WallContext;
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.WallSqlTableStat;
|
||||
import com.alibaba.druid.wall.WallVisitor;
|
||||
import com.alibaba.druid.wall.violation.ErrorCode;
|
||||
import com.alibaba.druid.wall.violation.IllegalSQLObjectViolation;
|
||||
|
||||
@@ -885,6 +881,10 @@ public class WallVisitorUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
String tableName = x.getTableName().getSimpleName();
|
||||
Set<String> updateCheckColumns = config.getUpdateCheckTable(tableName);
|
||||
boolean isUpdateCheckTable = updateCheckColumns != null && !updateCheckColumns.isEmpty();
|
||||
|
||||
SQLExpr where = x.getWhere();
|
||||
if (where == null) {
|
||||
WallContext context = WallContext.current();
|
||||
@@ -905,7 +905,6 @@ public class WallVisitorUtils {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
where.setParent(x);
|
||||
checkCondition(visitor, where);
|
||||
|
||||
if (Boolean.TRUE == getConditionValue(visitor, where, config.isUpdateWhereAlayTrueCheck())) {
|
||||
@@ -913,6 +912,70 @@ public class WallVisitorUtils {
|
||||
addViolation(visitor, ErrorCode.ALWAYS_TRUE, "update alway true condition not allow", x);
|
||||
}
|
||||
}
|
||||
|
||||
WallUpdateCheckHandler updateCheckHandler = config.getUpdateCheckHandler();
|
||||
if (isUpdateCheckTable && updateCheckHandler != null) {
|
||||
String checkColumn = updateCheckColumns.iterator().next();
|
||||
|
||||
SQLExpr valueExpr = null;
|
||||
for (SQLUpdateSetItem item : x.getItems()) {
|
||||
if (item.columnMatch(checkColumn)) {
|
||||
valueExpr = item.getValue();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (valueExpr != null) {
|
||||
List<SQLExpr> conditions;
|
||||
if (where instanceof SQLBinaryOpExpr) {
|
||||
conditions = SQLBinaryOpExpr.split((SQLBinaryOpExpr) where, SQLBinaryOperator.BooleanAnd);
|
||||
} else {
|
||||
conditions = new ArrayList<SQLExpr>();
|
||||
conditions.add(where);
|
||||
}
|
||||
|
||||
List<SQLExpr> filterValueExprList = new ArrayList<SQLExpr>();
|
||||
for (SQLExpr condition : conditions) {
|
||||
if (condition instanceof SQLBinaryOpExpr) {
|
||||
SQLBinaryOpExpr binaryCondition = (SQLBinaryOpExpr) condition;
|
||||
if (binaryCondition.getOperator() == SQLBinaryOperator.Equality
|
||||
&& binaryCondition.conditionContainsColumn(checkColumn)) {
|
||||
SQLExpr left = binaryCondition.getLeft();
|
||||
SQLExpr right = binaryCondition.getRight();
|
||||
|
||||
if (left instanceof SQLValuableExpr || left instanceof SQLVariantRefExpr) {
|
||||
filterValueExprList.add(left);
|
||||
} else if (right instanceof SQLValuableExpr || right instanceof SQLVariantRefExpr) {
|
||||
filterValueExprList.add(right);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boolean allValue = valueExpr instanceof SQLValuableExpr;
|
||||
if (allValue) {
|
||||
for (SQLExpr filterValue : filterValueExprList) {
|
||||
if (!(filterValue instanceof SQLValuableExpr)) {
|
||||
allValue = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (allValue) {
|
||||
Object setValue = ((SQLValuableExpr) valueExpr).getValue();
|
||||
List<Object> filterValues = new ArrayList<Object>(filterValueExprList.size());
|
||||
for (SQLExpr expr : filterValueExprList) {
|
||||
filterValues.add(((SQLValuableExpr) expr).getValue());
|
||||
}
|
||||
boolean validate = updateCheckHandler.check(tableName, checkColumn, setValue, filterValues);
|
||||
visitor.addViolation(new IllegalSQLObjectViolation(ErrorCode.UPDATE_CHECK_FAIL, "update check failed.", visitor.toSQL(x)));
|
||||
} else {
|
||||
visitor.addWallUpdateCheckItem(new WallUpdateCheckItem(tableName, checkColumn, valueExpr, filterValueExprList));
|
||||
}
|
||||
//updateCheckHandler.check(tableName, checkColumn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkUpdateForMultiTenant(visitor, x);
|
||||
|
||||
@@ -85,5 +85,7 @@ public interface ErrorCode {
|
||||
|
||||
public final static int COMPOUND = 8000;
|
||||
|
||||
public final static int UPDATE_CHECK_FAIL = 9000;
|
||||
|
||||
public final static int OTHER = 9999;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
package com.alibaba.druid.bvt.filter.wall;
|
||||
|
||||
import com.alibaba.druid.wall.WallCheckResult;
|
||||
import com.alibaba.druid.wall.WallConfig;
|
||||
import com.alibaba.druid.wall.WallUpdateCheckHandler;
|
||||
import com.alibaba.druid.wall.spi.MySqlWallProvider;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* Created by wenshao on 13/08/2017.
|
||||
*/
|
||||
public class WallUpdateCheckTest extends TestCase {
|
||||
private MySqlWallProvider wallProvider = new MySqlWallProvider();
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
Properties properties = new Properties();
|
||||
properties.put("druid.wall.updateCheckColumns", "t_orders.status");
|
||||
wallProvider.getConfig().configFromProperties(properties);
|
||||
wallProvider.getConfig().setUpdateCheckHandler(new WallUpdateCheckHandler() {
|
||||
|
||||
@Override
|
||||
public boolean check(String table, String column, Object setValue, List<Object> filterValues) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
public void test_update_check_handler() throws Exception {
|
||||
WallCheckResult result = wallProvider.check("update t_orders set status = 3 where id = 3 and status = 4");
|
||||
assertTrue(result.getViolations().size() > 0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user