以下代码来自SAMATE Reference Dataset。我用它来测试静态分析工具。 正如您所看到的,代码应该通过使用清理方法以及使用预准备语句来阻止SQL注入。
由于SCA工具无法知道自定义santitzation方法,因此不会检测到allowed
方法用于防止注入。
public class SQLInjection_good_089 extends HttpServlet
{
private static final long serialVersionUID = 1L;
public SQLInjection_good_089()
{
super();
}
// Table of allowed names to use
final String allowed_names[] = { "Mickael", "Mary",
"Peter", "Laura", "John"};
// Function to check if the current name takes part of the allowed ones
public boolean allowed( String in )
{
boolean bool = false;
for( int i = 0; i < 5; i++ )
{
if( in.equals( allowed_names[i] ) )
{
// the current name is allowed to use
bool = true;
break;
}
}
return bool;
}
// Method which will be called to handle HTTP GET requests
protected void doGet( HttpServletRequest req, HttpServletResponse resp )
throws ServletException, IOException
{
// Initialize the output stream
resp.setContentType("text/html");
ServletOutputStream out = resp.getOutputStream();
out.println("<HTML><BODY><blockquote><pre>");
Connection conn = null;
// Get the parameter "name" from the data provided by the user
String name = req.getParameter( "name" );
if ( (name != null) && (allowed(name) == true) )
{
try
{
// Set the context factory to use to create the initial context
System.setProperty (Context.INITIAL_CONTEXT_FACTORY, "your.ContextFactory");
// Create the initial context and use it to lookup the data source
InitialContext ic = new InitialContext ();
DataSource dataSrc = (DataSource) ic.lookup ("java:comp/env/jdbc:/mydb");
// Create a connection to the SQL database from the data source
conn = dataSrc.getConnection();
// Send a SQL request to the database
PreparedStatement ps = conn.prepareStatement( "SELECT * FROM users WHERE firstname LIKE ?" );
// replace the first parameter by name
ps.setString(1, name);
ps.executeQuery();
}
catch( NamingException e )
{
out.println( "Naming exception");
}
catch( SQLException e )
{
out.println( "SQL exception");
}
finally
{
try
{
if (conn != null)
conn.close ();
}
catch (SQLException se)
{
out.println("SQL Exception");
}
}
}
else
return;
out.println( "</pre></blockquote></body></html>" );
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
}
}
尽管如此,我认为使用预先准备好的声明应该可以防止注入此处。我错了吗?
答案 0 :(得分:3)
你是对的。 prepared statement
将处理'非法'SQL输入。
allowed(...)
函数是业务规则方面的一种用户输入验证,而不是防止SQL注入。
答案 1 :(得分:3)
是这里准备好的语句将阻止sql注入。这是因为您在查询中使用占位符(?)。这是占位符,这里需要注意。
以下是准备好的陈述的2个例子。第一个不会阻止sql注入。
PreparedStatement ps = conn.prepareStatement(“SELECT * FROM users WHERE firstname LIKE”+ name);
以上语句即使是准备语句也不会阻止sql注入
但是下面编写的语句可以防止sql注入。
PreparedStatement ps = conn.prepareStatement(“SELECT * FROM users WHERE firstname LIKE?”);
第一个和第二个语句的区别是,第一种情况下的查询是在运行时动态编译的,但在第二种情况下是预编译的。
这意味着像(a'or'1'='1)这样的恶意用户输入可以在第一个语句中改变查询。但是,自预编译以来的第二个查询将恶意用户输入视为数据,而不是sql命令。
简而言之,当且仅当它们与占位符和绑定参数一起使用时,Preapred语句才会阻止SQL注入。
答案 2 :(得分:1)
准备好的语句应该足以阻止SQL注入......
但是,如果您打算将用户消息写入'out'参数(例如out.printf( "Invalid username %s", name )
),请注意javascript注入。我可以输入我的姓名<script>alert('hi')</script>
答案 3 :(得分:1)
似乎阻止了SQL注入。你当然是正确的,allow()函数有帮助,但不完全是首选的方法。由于您的代码只是一个示例,我认为在现实世界中大多数程序将允许超过5种可能的选项。