mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
improve sql wall
This commit is contained in:
@@ -15,12 +15,14 @@
|
||||
*/
|
||||
package com.alibaba.druid.sql;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.druid.DruidRuntimeException;
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
import com.alibaba.druid.sql.ast.SQLObject;
|
||||
import com.alibaba.druid.sql.ast.SQLStatement;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
|
||||
import com.alibaba.druid.sql.dialect.db2.visitor.DB2OutputVisitor;
|
||||
import com.alibaba.druid.sql.dialect.db2.visitor.DB2SchemaStatVisitor;
|
||||
import com.alibaba.druid.sql.dialect.mysql.visitor.MySqlOutputVisitor;
|
||||
@@ -44,7 +46,6 @@ import com.alibaba.druid.util.JdbcConstants;
|
||||
import com.alibaba.druid.util.JdbcUtils;
|
||||
import com.alibaba.druid.util.StringUtils;
|
||||
|
||||
|
||||
public class SQLUtils {
|
||||
|
||||
private final static Log LOG = LogFactory.getLog(SQLUtils.class);
|
||||
@@ -63,7 +64,7 @@ public class SQLUtils {
|
||||
if (JdbcUtils.POSTGRESQL.equals(dbType)) {
|
||||
return toPGString(sqlObject);
|
||||
}
|
||||
|
||||
|
||||
if (JdbcUtils.DB2.equals(dbType)) {
|
||||
return toDB2String(sqlObject);
|
||||
}
|
||||
@@ -114,7 +115,7 @@ public class SQLUtils {
|
||||
String sql = out.toString();
|
||||
return sql;
|
||||
}
|
||||
|
||||
|
||||
public static String toDB2String(SQLObject sqlObject) {
|
||||
StringBuilder out = new StringBuilder();
|
||||
sqlObject.accept(new DB2OutputVisitor(out));
|
||||
@@ -211,7 +212,7 @@ public class SQLUtils {
|
||||
if (JdbcUtils.SQL_SERVER.equals(dbType) || JdbcUtils.JTDS.equals(dbType)) {
|
||||
return new SQLServerOutputVisitor(out);
|
||||
}
|
||||
|
||||
|
||||
if (JdbcUtils.DB2.equals(dbType)) {
|
||||
return new DB2OutputVisitor(out);
|
||||
}
|
||||
@@ -257,37 +258,53 @@ public class SQLUtils {
|
||||
}
|
||||
return stmtList;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* @author owenludong.lud
|
||||
* @param columnName
|
||||
* @param tableAlias
|
||||
* @param pattern if pattern is null,it will be set {%Y-%m-%d %H:%i:%s} as mysql default value and set {yyyy-mm-dd hh24:mi:ss} as oracle default value
|
||||
* @param dbType {@link JdbcConstants} if dbType is null ,it will be set the mysql as a default value
|
||||
* @param columnName
|
||||
* @param tableAlias
|
||||
* @param pattern if pattern is null,it will be set {%Y-%m-%d %H:%i:%s} as mysql default value and set {yyyy-mm-dd
|
||||
* hh24:mi:ss} as oracle default value
|
||||
* @param dbType {@link JdbcConstants} if dbType is null ,it will be set the mysql as a default value
|
||||
*/
|
||||
public static String buildToDate(String columnName,String tableAlias,String pattern,String dbType){
|
||||
StringBuilder sql = new StringBuilder();
|
||||
if(StringUtils.isEmpty(columnName))
|
||||
return "";
|
||||
if(StringUtils.isEmpty(dbType)) dbType = JdbcConstants.MYSQL;
|
||||
String formatMethod = "";
|
||||
if(JdbcConstants.MYSQL.equalsIgnoreCase(dbType)){
|
||||
formatMethod = "STR_TO_DATE";
|
||||
if(StringUtils.isEmpty(pattern)) pattern = "%Y-%m-%d %H:%i:%s";
|
||||
}else if(JdbcConstants.ORACLE.equalsIgnoreCase(dbType)){
|
||||
formatMethod = "TO_DATE";
|
||||
if(StringUtils.isEmpty(pattern)) pattern = "yyyy-mm-dd hh24:mi:ss";
|
||||
}else{
|
||||
return "";
|
||||
//expand date's handle method for other database
|
||||
}
|
||||
sql.append(formatMethod).append("(");
|
||||
if(!StringUtils.isEmpty(tableAlias))
|
||||
sql.append(tableAlias).append(".");
|
||||
sql.append(columnName).append(",");
|
||||
sql.append("'");
|
||||
sql.append(pattern);
|
||||
sql.append("')");
|
||||
return sql.toString();
|
||||
}
|
||||
public static String buildToDate(String columnName, String tableAlias, String pattern, String dbType) {
|
||||
StringBuilder sql = new StringBuilder();
|
||||
if (StringUtils.isEmpty(columnName)) return "";
|
||||
if (StringUtils.isEmpty(dbType)) dbType = JdbcConstants.MYSQL;
|
||||
String formatMethod = "";
|
||||
if (JdbcConstants.MYSQL.equalsIgnoreCase(dbType)) {
|
||||
formatMethod = "STR_TO_DATE";
|
||||
if (StringUtils.isEmpty(pattern)) pattern = "%Y-%m-%d %H:%i:%s";
|
||||
} else if (JdbcConstants.ORACLE.equalsIgnoreCase(dbType)) {
|
||||
formatMethod = "TO_DATE";
|
||||
if (StringUtils.isEmpty(pattern)) pattern = "yyyy-mm-dd hh24:mi:ss";
|
||||
} else {
|
||||
return "";
|
||||
// expand date's handle method for other database
|
||||
}
|
||||
sql.append(formatMethod).append("(");
|
||||
if (!StringUtils.isEmpty(tableAlias)) sql.append(tableAlias).append(".");
|
||||
sql.append(columnName).append(",");
|
||||
sql.append("'");
|
||||
sql.append(pattern);
|
||||
sql.append("')");
|
||||
return sql.toString();
|
||||
}
|
||||
|
||||
public static List<SQLExpr> split(SQLBinaryOpExpr x) {
|
||||
List<SQLExpr> groupList = new ArrayList<SQLExpr>();
|
||||
SQLExpr left = x.getLeft();
|
||||
for (;;) {
|
||||
if (left instanceof SQLBinaryOpExpr && ((SQLBinaryOpExpr) left).getOperator() == x.getOperator()) {
|
||||
SQLBinaryOpExpr binaryLeft = (SQLBinaryOpExpr) left;
|
||||
groupList.add(binaryLeft.getRight());
|
||||
left = binaryLeft.getLeft();
|
||||
} else {
|
||||
groupList.add(left);
|
||||
break;
|
||||
}
|
||||
}
|
||||
groupList.add(x.getRight());
|
||||
return groupList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,13 @@ public class SQLMethodInvokeExpr extends SQLExprImpl implements Serializable {
|
||||
public List<SQLExpr> getParameters() {
|
||||
return this.parameters;
|
||||
}
|
||||
|
||||
public void addParameter(SQLExpr param) {
|
||||
if (param != null) {
|
||||
param.setParent(this);
|
||||
}
|
||||
this.parameters.add(param);
|
||||
}
|
||||
|
||||
public void output(StringBuffer buf) {
|
||||
if (this.owner != null) {
|
||||
|
||||
@@ -229,10 +229,10 @@ public class MySqlExprParser extends SQLExprParser {
|
||||
}
|
||||
} else if (expr instanceof SQLCharExpr) {
|
||||
SQLMethodInvokeExpr concat = new SQLMethodInvokeExpr("CONCAT");
|
||||
concat.getParameters().add(expr);
|
||||
concat.addParameter(expr);
|
||||
do {
|
||||
String chars = lexer.stringVal();
|
||||
concat.getParameters().add(new SQLCharExpr(chars));
|
||||
concat.addParameter(new SQLCharExpr(chars));
|
||||
lexer.nextToken();
|
||||
} while (lexer.token() == Token.LITERAL_CHARS);
|
||||
expr = concat;
|
||||
@@ -325,7 +325,7 @@ public class MySqlExprParser extends SQLExprParser {
|
||||
SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr(ident);
|
||||
for (;;) {
|
||||
SQLExpr param = expr();
|
||||
methodInvokeExpr.getParameters().add(param);
|
||||
methodInvokeExpr.addParameter(param);
|
||||
|
||||
if (lexer.token() == Token.COMMA) {
|
||||
lexer.nextToken();
|
||||
@@ -333,12 +333,12 @@ public class MySqlExprParser extends SQLExprParser {
|
||||
} else if (lexer.token() == Token.FROM) {
|
||||
lexer.nextToken();
|
||||
SQLExpr from = expr();
|
||||
methodInvokeExpr.getParameters().add(from);
|
||||
methodInvokeExpr.addParameter(from);
|
||||
|
||||
if (lexer.token() == Token.FOR) {
|
||||
lexer.nextToken();
|
||||
SQLExpr forExpr = expr();
|
||||
methodInvokeExpr.getParameters().add(forExpr);
|
||||
methodInvokeExpr.addParameter(forExpr);
|
||||
}
|
||||
break;
|
||||
} else if (lexer.token() == Token.RPAREN) {
|
||||
@@ -371,7 +371,7 @@ public class MySqlExprParser extends SQLExprParser {
|
||||
}
|
||||
|
||||
SQLExpr param = expr();
|
||||
methodInvokeExpr.getParameters().add(param);
|
||||
methodInvokeExpr.addParameter(param);
|
||||
|
||||
if (lexer.token() == Token.FROM) {
|
||||
lexer.nextToken();
|
||||
@@ -461,8 +461,8 @@ public class MySqlExprParser extends SQLExprParser {
|
||||
accept(Token.RPAREN);
|
||||
|
||||
SQLMethodInvokeExpr locate = new SQLMethodInvokeExpr("LOCATE");
|
||||
locate.getParameters().add(subStr);
|
||||
locate.getParameters().add(str);
|
||||
locate.addParameter(subStr);
|
||||
locate.addParameter(str);
|
||||
|
||||
expr = locate;
|
||||
return primaryRest(expr);
|
||||
@@ -502,7 +502,7 @@ public class MySqlExprParser extends SQLExprParser {
|
||||
|
||||
SQLMethodInvokeExpr methodInvokeExpr = new SQLMethodInvokeExpr("INTERVAL");
|
||||
if (lexer.token() != Token.RPAREN) {
|
||||
exprList(methodInvokeExpr.getParameters());
|
||||
exprList(methodInvokeExpr.getParameters(), methodInvokeExpr);
|
||||
}
|
||||
|
||||
accept(Token.RPAREN);
|
||||
|
||||
@@ -667,7 +667,7 @@ public class SQLExprParser extends SQLParser {
|
||||
lexer.nextToken();
|
||||
} else {
|
||||
if (lexer.token() == Token.PLUS) {
|
||||
methodInvokeExpr.getParameters().add(new SQLIdentifierExpr("+"));
|
||||
methodInvokeExpr.addParameter(new SQLIdentifierExpr("+"));
|
||||
lexer.nextToken();
|
||||
} else {
|
||||
exprList(methodInvokeExpr.getParameters(), methodInvokeExpr);
|
||||
|
||||
@@ -22,12 +22,12 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.druid.sql.SQLUtils;
|
||||
import com.alibaba.druid.sql.ast.SQLExpr;
|
||||
import com.alibaba.druid.sql.ast.SQLName;
|
||||
import com.alibaba.druid.sql.ast.SQLObject;
|
||||
@@ -44,6 +44,7 @@ import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLInListExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLInSubQueryExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLIntegerExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLLiteralExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLMethodInvokeExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLNCharExpr;
|
||||
import com.alibaba.druid.sql.ast.expr.SQLNotExpr;
|
||||
@@ -121,7 +122,27 @@ public class WallVisitorUtils {
|
||||
}
|
||||
|
||||
public static void check(WallVisitor visitor, SQLBinaryOpExpr x) {
|
||||
|
||||
if (x.getOperator() == SQLBinaryOperator.Add || x.getOperator() == SQLBinaryOperator.Concat) {
|
||||
List<SQLExpr> groupList = SQLUtils.split(x);
|
||||
if (groupList.size() >= 4) {
|
||||
int chrCount = 0;
|
||||
for (int i = 0; i < groupList.size(); ++i) {
|
||||
SQLExpr item = groupList.get(i);
|
||||
if (item instanceof SQLMethodInvokeExpr) {
|
||||
SQLMethodInvokeExpr methodExpr = (SQLMethodInvokeExpr) item;
|
||||
String methodName = methodExpr.getMethodName().toLowerCase();
|
||||
if ("chr".equals(methodName) || "char".equals(methodName)) {
|
||||
if (methodExpr.getParameters().get(0) instanceof SQLLiteralExpr) {
|
||||
chrCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chrCount >= 4) {
|
||||
addViolation(visitor, ErrorCode.EVIL_CONCAT, "evil concat", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void check(WallVisitor visitor, SQLCreateTableStatement x) {
|
||||
@@ -232,7 +253,7 @@ public class WallVisitorUtils {
|
||||
if (queryBlockFromIsNull(visitor, x, false)) {
|
||||
addViolation(visitor, ErrorCode.EMPTY_QUERY_HAS_CONDITION, "empty select has condition", x);
|
||||
}
|
||||
|
||||
|
||||
boolean isSimpleConstExpr = false;
|
||||
SQLExpr first = getFirst(where);
|
||||
|
||||
@@ -616,19 +637,7 @@ public class WallVisitorUtils {
|
||||
}
|
||||
|
||||
if (x.getOperator() == SQLBinaryOperator.BooleanOr) {
|
||||
List<SQLExpr> groupList = new ArrayList<SQLExpr>();
|
||||
SQLExpr left = x.getLeft();
|
||||
for (;;) {
|
||||
if (left instanceof SQLBinaryOpExpr && ((SQLBinaryOpExpr) left).getOperator() == x.getOperator()) {
|
||||
SQLBinaryOpExpr binaryLeft = (SQLBinaryOpExpr) left;
|
||||
groupList.add(binaryLeft.getRight());
|
||||
left = binaryLeft.getLeft();
|
||||
} else {
|
||||
groupList.add(left);
|
||||
break;
|
||||
}
|
||||
}
|
||||
groupList.add(x.getRight());
|
||||
List<SQLExpr> groupList = SQLUtils.split(x);
|
||||
|
||||
boolean allFalse = true;
|
||||
for (int i = groupList.size() - 1; i >= 0; --i) {
|
||||
@@ -1108,7 +1117,7 @@ public class WallVisitorUtils {
|
||||
|
||||
checkSchema(visitor, x.getOwner());
|
||||
|
||||
String methodName = x.getMethodName();
|
||||
String methodName = x.getMethodName().toLowerCase();
|
||||
if (!visitor.getProvider().checkDenyTable(methodName)) {
|
||||
if (isTopUpdateStatement(x) || isFirstSelectTableSource(x)) {
|
||||
if (topStatementContext != null) {
|
||||
@@ -1132,7 +1141,7 @@ public class WallVisitorUtils {
|
||||
return;
|
||||
}
|
||||
|
||||
String methodName = x.getMethodName();
|
||||
String methodName = x.getMethodName().toLowerCase();
|
||||
|
||||
WallContext context = WallContext.current();
|
||||
if (context != null) {
|
||||
|
||||
@@ -66,6 +66,7 @@ public interface ErrorCode {
|
||||
public final static int CONST_CASE_CONDITION = 2109;
|
||||
public final static int EVIL_HINTS = 2110;
|
||||
public final static int EVIL_NAME = 2111;
|
||||
public final static int EVIL_CONCAT = 2112;
|
||||
|
||||
public final static int NOT_PARAMETERIZED = 2200;
|
||||
public final static int MULTI_TENANT = 2201;
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 1999-2011 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.bvt.filter.wall;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.spi.MySqlWallProvider;
|
||||
|
||||
public class MySqlWallTest140 extends TestCase {
|
||||
|
||||
public void test_false() throws Exception {
|
||||
WallProvider provider = new MySqlWallProvider();
|
||||
|
||||
String sql = "SELECT name, '******' password, createTime from user " //
|
||||
+ "where name like 'admin%' "//
|
||||
+ " AND 8600=CONVERT(INT,(SELECT CHAR(113)+CHAR(118)+CHAR(100)+CHAR(114)+CHAR(113)+(SELECT (CASE WHEN (8600=8600) THEN CHAR(49) ELSE CHAR(48) END))+CHAR(113)+CHAR(118)+CHAR(98)+CHAR(97)+CHAR(113))) AND '%'=''";
|
||||
Assert.assertFalse(provider.checkValid(sql));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright 1999-2011 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.bvt.filter.wall;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
||||
import com.alibaba.druid.wall.WallProvider;
|
||||
import com.alibaba.druid.wall.spi.MySqlWallProvider;
|
||||
|
||||
public class MySqlWallTest141 extends TestCase {
|
||||
|
||||
public void test_false() throws Exception {
|
||||
WallProvider provider = new MySqlWallProvider();
|
||||
|
||||
String sql = "SELECT name, '******' password, createTime from user where length(name) = 999999.9 or ascii(substring((database()),1,1))=127";
|
||||
Assert.assertFalse(provider.checkValid(sql));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user