\
转换为\
,将任何"
转换为\"
,将任何'
转换为\'
,并将任何\n
转换为\n
,以便在通过MySQL SQL注入评估字符串时将其阻塞。 我已经整理了一些正在使用的代码,该函数中的所有
\\\\\\
都使我发疯。如果有人碰巧有这个例子,我将不胜感激。#1 楼
PreparedStatement是可行的方法,因为它们使SQL注入成为不可能。这是一个简单的示例,将用户的输入作为参数:public insertUser(String name, String email) {
Connection conn = null;
PreparedStatement stmt = null;
try {
conn = setupTheDatabaseConnectionSomehow();
stmt = conn.prepareStatement("INSERT INTO person (name, email) values (?, ?)");
stmt.setString(1, name);
stmt.setString(2, email);
stmt.executeUpdate();
}
finally {
try {
if (stmt != null) { stmt.close(); }
}
catch (Exception e) {
// log this error
}
try {
if (conn != null) { conn.close(); }
}
catch (Exception e) {
// log this error
}
}
}
无论姓名和电子邮件中包含什么字符,这些字符都将直接放置在数据库中。它们不会以任何方式影响INSERT语句。
对于不同的数据类型,有不同的设置方法-使用哪种方法取决于数据库字段是什么。例如,如果数据库中有一个INTEGER列,则应使用
setInt
方法。 PreparedStatement文档列出了可用于设置和获取数据的所有不同方法。评论
通过这种方法,您可以将每个参数都视为字符串并且仍然安全吗?我正在尝试寻找一种方法来更新我现有的体系结构以使其安全,而不必重建整个数据库层。
–斯科特·邦纳(Scott Bonner)
09年11月28日在16:34
所有的动态SQL都只是字符串,因此这不是要问的问题。我对PrepareStatement并不熟悉,所以真正的问题是它是否会生成可以通过ExecuteUpdate执行的参数化查询。如果是,那很好。如果不是,那么这只是在隐藏问题,除了重新设计数据库层之外,您可能没有其他安全选择。从一开始就必须要处理SQL注入。这不是您以后可以轻松添加的内容。
– Cylon Cat
09年11月28日在16:52
如果要插入INTEGER字段,则需要使用'setInt'。同样,其他数字数据库字段将使用其他设置器。我发布了指向PreparedStatement文档的链接,该文档列出了所有设置器类型。
– Kaleb Brasee
09年11月28日在16:52
是的,Cylon,PreparedStatements生成参数化查询。
– Kaleb Brasee
09年11月28日在16:54
@Kaleb Brasee,谢谢。很高兴知道。这些工具在每种环境中都不同,但是深入了解参数化查询是基本答案。
– Cylon Cat
09年11月28日在17:01
#2 楼
防止SQL注入的唯一方法是使用参数化SQL。根本不可能建立一个比以SQL为生的人更聪明的过滤器。因此,请为所有输入,更新和where子句使用参数。动态SQL只是向黑客敞开的大门,并且在存储过程中包含了动态SQL。参数化,参数化,参数化。
评论
甚至参数化的SQL也不是100%保证。但这是一个很好的开始。
–duffymo
09年11月28日在16:37
@duffymo,我同意没有什么是100%安全的。您是否有一个即使在参数化SQL下也可以使用的SQL注入示例?
– Cylon Cat
09年11月28日在16:42
@Cylon Cat:当然,当将一部分SQL(例如@WhereClause或@tableName)作为参数传递,串联到SQL中并动态执行时。当您让用户编写代码时,就会发生SQL注入。是否捕获它们的代码作为参数都没有关系。
–史蒂夫·卡斯(Steve Kass)
09年11月28日在17:46
顺便说一句,我不知道为什么这里不多提及,但是使用PreparedStatements也更容易并且更具可读性。仅此一点就可能使它们成为所有了解它们的程序员的默认设置。
–伊丹·莫尔
09年11月28日在18:54
请注意,某些数据库的PreparedStatement创建起来非常昂贵,因此,如果您需要做很多事情,请同时测量这两种类型。
–索比昂·拉文·安徒生
09年11月29日在8:44
#3 楼
如果确实不能使用防御选项1:预准备语句(参数化查询)或防御选项2:存储过程,则不要构建自己的工具,请使用OWASP Enterprise Security API。在Google代码上托管的OWASP ESAPI中:不要编写自己的安全控件!在为每个Web应用程序或Web服务开发安全控制时重新设计轮子会导致时间浪费和大量安全漏洞。 OWASP企业安全API(ESAPI)工具包可帮助软件开发人员防范与安全相关的设计和实现方面的缺陷。
有关更多详细信息,请参阅在Java中防止SQL注入和SQL注入预防速查表。
特别注意防御选项3:转义引入OWASP ESAPI项目的所有用户提供的输入)。
评论
截至今天,ESAPI似乎已失效。在AWS上有WAF可以帮助防止SQL注入,XSS等。此时是否还有其他替代方法?
–ChrisOdney
17年5月31日在8:19
@ChrisOdney可以轻松绕过WAF。大多数框架已经实现了自己的SQL注入预防措施,在这种预防措施中,它们自己自动逃逸了参数。遗留项目的替代方案:owasp.org/index.php/…
– JavanR。
19年2月25日在12:40
#4 楼
(这是对OP在原始问题下的评论的回答;我完全同意PreparedStatement是用于此工作的工具,而不是正则表达式。)当您说
\n
时,是指序列\
+ n
或实际的换行符?如果是\
+ n
,则任务非常简单:s = s.replaceAll("['\"\\]", "\\s = s.replaceAll("\n", "\\n");
");
要在输入中匹配一个反斜杠,请将其中四个反斜杠放在正则表达式字符串中。要在输出中添加一个反斜杠,请将其中四个添加到替换字符串中。这是假设您正在以Java String文字形式创建正则表达式和替换。如果您以其他方式创建它们(例如,通过从文件中读取它们),则不必进行所有的两次转义操作。
如果输入中包含换行符并且您想要将其替换为转义序列,可以使用以下命令对输入进行第二遍处理:
s = s.replaceAll("\n", "\\\\n");
或者也许您想要两个反斜杠(我不太喜欢清楚):
q4312078q
评论
感谢您的评论,我喜欢您将所有字符组合在一起的方式,我打算用一种不太正规的表达方式来替换所有字符...我现在不确定如何分配此问题的答案。最终,PreparedStatement是答案,但就我当前的目标而言,您的答案就是我需要的答案,如果我将答案提供给较早的准备语句的答案之一,或者您有办法在一对夫妇之间共享答案,您会感到不高兴吗?
–斯科特·邦纳(Scott Bonner)
09年12月2日在13:30
由于这只是暂时的麻烦,因此请继续接受PreparedStatement答案之一。
–艾伦·摩尔
09年12月2日在22:20
#5 楼
在大多数情况下(但不是在所有情况下),PreparedStatement是可以使用的方法。有时,您会发现自己必须构建查询或查询的一部分并将其存储为字符串以供以后使用。请查看OWASP网站上的SQL Injection Prevention备忘单,以获取更多详细信息和不同编程语言的API。评论
OWASP速查表已移至GitHub。 SQL注入备忘单现在在这里:github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/…
–theferrit32
19年7月15日在18:44
#6 楼
准备语句是最好的解决方案,但是如果您确实需要手动执行它,则也可以使用Apache Commons-Lang库中的StringEscapeUtils
类。它具有escapeSql(String)
方法,可以使用:import org.apache.commons.lang.StringEscapeUtils;
…
String escapedSQL = StringEscapeUtils.escapeSql(unescapedSQL);
评论
供参考:commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/…无论如何,此方法仅转义引号,并且似乎无法防止SQL Injection攻击。
– Paco Abato
17年5月3日,7:30
它已从最新版本中删除,因为它仅转义了单引号
– Pini Cheyni
17年5月20日在15:05
应删除此答案,因为它不会阻止sql注入。
– JavanR。
19-2-25在12:34
#7 楼
使用正则表达式删除可能导致SQL注入的文本听起来像是通过Statement
而不是PreparedStatement
将SQL语句发送到数据库。防止SQL注入的最简单方法之一首先是使用
PreparedStatement
,它使用占位符接受数据以替换为SQL语句,该占位符不依赖于字符串串联来创建要发送到数据库的SQL语句。有关更多信息信息,使用Java教程中的预处理语句将是一个不错的起点。
#8 楼
如果您使用的是旧版系统,或者您有太多地方可以在短时间内切换到PreparedStatement
s-即,如果在使用其他答案建议的最佳实践方面遇到障碍,则可以尝试使用AntiSQLFilter #9 楼
您需要以下代码。乍一看,这看起来像我编写的任何旧代码。但是,我所做的只是查看http://grepcode.com/file/repo1.maven.org/maven2/mysql/mysql-connector-java/5.1.31/com/mysql/jdbc/PreparedStatement的源代码。 Java的然后,在此之后,我仔细查看了setString(int parameterIndex,String x)的代码,以查找转义的字符并将其自定义为我自己的类,以便可以将其用于所需的目的。毕竟,如果这是Oracle逃逸的字符列表,那么知道这确实在安全方面令人放心。对于下一个主要的Java版本,也许Oracle需要轻推才能添加与此方法类似的方法。评论
我认为这段代码是上面链接中源代码的反编译版本。现在在较新的mysql-connector-java-xxx中,似乎已删除了大小写'\ u00a5'和大小写'\ u20a9'语句
– zhy
16-4-18在11:22
我用您的代码尝试了sqlmap,但是它并没有保护我免受首次攻击`类型:基于布尔的盲注标题:和基于布尔的盲注-WHERE或HAVING子句有效负载:q = 1%'AND 5430 = 5430 AND'%' ='`
– shareef
16年9月9日在11:46
对不起,它的工作正常,但是正在查看上次存储的会话结果。
– shareef
16年9月9日在12:52
您可以使用org.ostermiller.utils.StringHelper.escapeSQL()或com.aoindustries.sql.SQLUtility.escapeSQL()。
– Mohamed Ennahdi El Idrissi
16-10-4在16:43
重要的是,请注意复制原始代码的GPLv2许可证,以供遇到此问题的任何人使用。我不是律师,但我强烈建议您不要在项目中使用此答案,除非您完全意识到包含此许可代码的含义。
–尼克·史派克(Nick Spacek)
17年4月18日在11:52
#10 楼
在搜索了很多测试解决方案以防止sqlmap遭到sql注入后,如果遗留系统无法在所有位置应用准备好的语句。java-security-cross-site-scripting-xss-and-sql -injection主题
是解决方案
我尝试了@Richard的解决方案,但在我的情况下不起作用。
我使用了过滤器
该过滤器的目标是将请求包装到一个自己编码的包装器MyHttpRequestWrapper中,该包装器将具有特殊字符(<,>,',…)的HTTP参数转换为
通过org.springframework.web.util.HtmlUtils.htmlEscape(…)
方法的HTML
代码。注意:Apache Commons中有类似的类:
<filter>
<filter-name>RequestWrappingFilter</filter-name>
<filter-class>com.huo.filter.RequestWrappingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>RequestWrappingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
package com.huo.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletReponse;
import javax.servlet.http.HttpServletRequest;
public class RequestWrappingFilter implements Filter{
public void doFilter(ServletRequest req, ServletReponse res, FilterChain chain) throws IOException, ServletException{
chain.doFilter(new MyHttpRequestWrapper(req), res);
}
public void init(FilterConfig config) throws ServletException{
}
public void destroy() throws ServletException{
}
}
package com.huo.filter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.lang.StringEscapeUtils;
public class MyHttpRequestWrapper extends HttpServletRequestWrapper{
private Map<String, String[]> escapedParametersValuesMap = new HashMap<String, String[]>();
public MyHttpRequestWrapper(HttpServletRequest req){
super(req);
}
@Override
public String getParameter(String name){
String[] escapedParameterValues = escapedParametersValuesMap.get(name);
String escapedParameterValue = null;
if(escapedParameterValues!=null){
escapedParameterValue = escapedParameterValues[0];
}else{
String parameterValue = super.getParameter(name);
// HTML transformation characters
escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);
// SQL injection characters
escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);
escapedParametersValuesMap.put(name, new String[]{escapedParameterValue});
}//end-else
return escapedParameterValue;
}
@Override
public String[] getParameterValues(String name){
String[] escapedParameterValues = escapedParametersValuesMap.get(name);
if(escapedParameterValues==null){
String[] parametersValues = super.getParameterValues(name);
escapedParameterValue = new String[parametersValues.length];
//
for(int i=0; i<parametersValues.length; i++){
String parameterValue = parametersValues[i];
String escapedParameterValue = parameterValue;
// HTML transformation characters
escapedParameterValue = org.springframework.web.util.HtmlUtils.htmlEscape(parameterValue);
// SQL injection characters
escapedParameterValue = StringEscapeUtils.escapeSql(escapedParameterValue);
escapedParameterValues[i] = escapedParameterValue;
}//end-for
escapedParametersValuesMap.put(name, escapedParameterValues);
}//end-else
return escapedParameterValues;
}
}
评论
java-security-cross-site-scripting-xss和sql-injection主题好吗?我正在尝试为旧版应用程序找到解决方案。
–caot
6月12日14:38
#11 楼
如果您使用的是PL / SQL,还可以使用DBMS_ASSERT
,它可以清理您的输入,因此您可以使用它而不必担心SQL注入。
例如,请参见以下答案:
https://stackoverflow.com/a/21406499/1726419
#12 楼
来自:来源public String MysqlRealScapeString(String str){
String data = null;
if (str != null && str.length() > 0) {
str = str.replace("\", "\\");
str = str.replace("'", "\'");
str = str.replace("q4312078q", "\0");
str = str.replace("\n", "\n");
str = str.replace("\r", "\r");
str = str.replace("\"", "\\"");
str = str.replace("\x1a", "\Z");
data = str;
}
return data;
}
评论
警告:它为空字符串返回null,因此您可能会得到意外的结果:“从ITEM.value ='ITEM.ITEM中的ITEM.id到ITEM.id = Iull.item。而不是ITEM.value =''!
–izogfif
12月10日18:41
评论
好的,我得出的结论是,PreparedStatement是可行的方法,但是根据当前的目标,我需要按原计划进行操作,只是暂时设置一个过滤器,一旦达到当前的里程碑,我就可以返回并重构数据库以准备语句。在保持势头的同时,有人给出了一种解决方案,可以在Java的前提下有效地对MySQL的上述字符进行转义,而其正则表达式系统绝对难以确定所需的转义数....并非所有SQL语句都是可参数化的,例如“ SET ROLE role_name”或“ LISTEN channel_name”
@NeilMcGuigan是的。大多数驱动程序还将拒绝参数化诸如CREATE VIEW myview AS SELECT * FROM mytable WHERE col =吗?因为主语句是DDL语句,所以即使您要参数化的部分实际上是DML。