System.out.println(用户);
}
System.out.println(\’userList2:\’);
对于(用户user:userList2){
System.out.println(用户);
}
会话.关闭();
附寄();
}
显示执行结果。
![](https://img-blog.csdnimg.cn/img_convert/a3e258ee37b47eb45ab268c4f5342b5f.png)
查询您找到的所有内容
1.2 SQL注入问题
`${}` 会导致SQL 注入问题,但`#{}` 不会导致SQL 注入问题
我们来测试一下:
ListuserList2=userDao.findByUsername2(\’aaa\’or1=1\’);
System.out.println(\’userList2:\’);
对于(用户user:userList2){
System.out.println(用户);
}
查询生成的SQL语句:
![](https://img-blog.csdnimg.cn/img_convert/e9f09147797870985417d685cf2da327.png)
我传递的参数是aaa\’或1=1——并且查询了所有数据。
你能想象如果你想根据ID删除会发生什么吗?
deletefromuserwhereid=\’${value}\’
如果我通过的是:1\’或1=1——每个人都应该已经知道结果会是什么。
这里的ID是整数类型,所以无法测试。如果您有兴趣,请私下测试一下。
如果你使用上面的`#{}`,就不会有任何SQL注入问题。
![](https://img-blog.csdnimg.cn/img_convert/2ec3cde8bf0655ea5417855b0d439a37.png)
1.3 `${}` 和`#{}` 的区别
`#{}` 与JDBC 中的? 等价的占位符匹配。编译后,传递的值会添加双引号以防止SQL 注入问题。
`${}` 与传递的实际值匹配。一旦通过,SQL 语句和字符串就会组合在一起。由于${} 与其他SQL 字符串连接在一起,因此它不能防止SQL 注入问题。
显示`#{}`和`${}`生成的SQL语句。
![](https://img-blog.csdnimg.cn/img_convert/da2068fddd9856c931a520e151a25dbc.png)
Stringabc=“123”;
#{abc}=\”123\”
${值}=123;
1.4 `#{}`底层如何防止SQL注入?
1.4.1 网上的答案
网上关于此类问题的疑问很多,原因有两个。
**1)`#{}`底层使用的是预编译的PreparedStatement,所以不存在SQL注入问题。
其实预编译是MySQL本身的一个特性,与PreparedStatement无关。还有,预编译并不是我们理解的预编译,PreparedStatement底层默认是不使用预编译的(必须手动开启)。详情请参阅下文
**2) `${}` 会导致SQL 注入问题,因为`#{}` 不会产生字符串连接,但`${}` 会产生字符串连接。
这两个答案都经不起进一步的调查,最终的答案也只停留在表面,没有人知道其中的原因。
1.4.2 如何防止SQL注入?
让我们打开MySQL驱动程序源代码,看看会发生什么。
打开PreparedStatement类的setString()方法(MyBatis使用setString()方法向`#{}`传递参数,但不向`${}`传递参数)。
![](https://img-blog.csdnimg.cn/img_convert/a6c393020b2ed2e1cbc868d542eb978d.png)
setString()方法的所有源代码:
publicvoidsetString(intparameterIndex,Stringx)抛出SQLException{
同步(this.checkClosed()。getConnectionMutex()){
如果(x==null){
this.setNull(parameterIndex,1);
}除此之外{
this.checkClosed();
intstringLength=x.length();
字符串生成器缓冲区;
if(this.connection.isNoBackslashEscapesSet()){
booleanneedsHexEscape=this.isEscapeNeededForString(x,stringLength);
对象参数AsBytes;
byte[] 参数AsBytes;
if(!needsHexEscape){
参数AsBytes=null;
buf=newStringBuilder(x.length()+2);
buf.append(\’\’);
buf.append(x);
buf.append(\’\’);
if(!this.isLoadDataQuery){
参数AsBytes=StringUtils.getBytes(buf.toString(),this.charConverter,this.charEncoding,this.connection.getServerCharset(),this.connection.parserKnowsUnicode(),this.getExceptionInterceptor());
}除此之外{
参数AsBytes=StringUtils.getBytes(buf.toString());
}
this.setInternal(parameterIndex,parameterAsBytes);
}除此之外{
参数AsBytes=null;
if(!this.isLoadDataQuery){
parameterAsBytes=StringUtils.getBytes(x,this.charConverter,this.charEncoding,this.connection.getServerCharset(),this.connection.parserKnowsUnicode(),this.getExceptionInterceptor());
}除此之外{
参数AsBytes=StringUtils.getBytes(x);
}
this.setBytes(parameterIndex,parameterAsBytes);
}
返回;
}
字符串参数AsString=x;
booleanneedsQuoted=true;
if(this.isLoadDataQuery||this.isEscapeNeededForString(x,stringLength)){
需要引用=假;
buf=newStringBuilder((int)((double)x.length()*1.1D));
buf.append(\’\’);
for(inti=0;istringLength;++i){//遍历字符串得到每个字符
charc=x.charAt(i);
开关{
案例“\\u0000”:
buf.append(\’\\\’);
buf.append(\’0\’);
休息;
案例“\\n”:
buf.append(\’\\\’);
buf.append(\’n\’);
休息;
案例“\\r”:
buf.append(\’\\\’);
buf.append(\’r\’);
休息;
案例“\\u001a”:
buf.append(\’\\\’);
buf.append(\’Z\’);
休息;
案例\’\’:
if(this.usingAnsiMode){
buf.append(\’\\\’);
}
buf.append(\’\’\’\’);
休息;
案例\’\’:
buf.append(\’\\\’);
buf.append(\’\’);
休息;
案例“\\”:
buf.append(\’\\\’);
buf.append(\’\\\’);
休息;
案例“”:
外壳‘’:
if(this.charsetEncoder!=null){
CharBuffercbuf=CharBuffer.allocate(1);
ByteBufferbbuf=ByteBuffer.分配(1);
cbuf.put;
cbuf.position(0);
this.charsetEncoder.encode(cbuf,bbuf,true);
if(bbuf.get(0)==92){
buf.append(\’\\\’);
}
}
buf.append;
休息;
默认:
buf.append;
}
}
buf.append(\’\’);
参数AsString=buf.toString();
}
缓冲区=空;
byte[] 参数AsBytes;
if(!this.isLoadDataQuery){
if(需要引用){
parameterAsBytes=StringUtils.getBytesWrapped(parameterAsString,‘’’,‘’’,this.charConverter,this.charEncoding,this.connection.getServerCharset(),this.connection.parserKnowsUnicode(),this.getExceptionInterceptor());
}除此之外{
parameterAsBytes=StringUtils.getBytes(parameterAsString,this.charConverter,this.charEncoding,this.connection.getServerCharset(),this.connection.parserKnowsUnicode(),this.getExceptionInterceptor());
}
}除此之外{
参数AsBytes=StringUtils.getBytes(参数AsString);
}
this.setInternal(parameterIndex,parameterAsBytes);
this.parameterTypes[parameterIndex-1+this.getParameterIndexOffset()]=12;
}
}
}
执行`#{}`查询语句并观察断点。
![](https://img-blog.csdnimg.cn/img_convert/e8a97e476e1d5c4ba91f1984117ce301.png)
最终传递的参数为:
![](https://img-blog.csdnimg.cn/img_convert/e85268d0538b53b907cf84e642d4fc85.png)
最后传递的参数是:`\’aaa\\\’ or 1=1 –`
在数据库上运行以下SQL语句(无法查询到数据)。
select*fromuserwhereusernamelike\’aaa\’or1=1\’
![](https://img-blog.csdnimg.cn/img_convert/9692adb53a2af887dacb01a5629e6044.png)
如果我们删除添加到PreparedStatement 中的“/”会发生什么?让我们运行SQL。
select*fromuserwhereusernamelike\’aaa\’or1=1\’
![](https://img-blog.csdnimg.cn/img_convert/7355a91010d025251e46441377bad95a.png)
还可以通过MySQL日志观察`#{}`和`${}`生成的SQL语句来分析问题。
**1) 打开MySQL 日志记录:**
在MySQL配置文件的[mysqld]下添加以下配置:
#是否启用mysql日志0: off (默认值) 1: on
通用日志=1
哪个兄弟不知道你可以提前回答网络安全面试问题?我们整理了160多道网络安全面试题(金9银10),让你的网络安全面试脱颖而出,我花了一周的时间才完成。做吧。
工程师王兰一面试题及答案目前只对我哥有用。如果你能正确回答70% 的问题,那么你找到一份稳定的工作就不会有太大困难。
对于有1-3年工作经验后想换工作的朋友来说,这也是一个很好的资源!
【如何获取完整版在文末! ]
93 网络安全面试问题
我就不一一截图了,因为内容太多了。
黑客学习资源推荐
最后给大家分享一套完整的网络安全学习资料,对所有想学习网络安全的人都有用。
对于刚接触网络安全的学生,我们创建了详细的学习和成长路线图。这可以说是最科学、最系统的学习路线。每个人都可以遵循这个大方向。
朋友们,如果您需要的话,请联系我们获取~
1零基础入门
学习路线
对于刚接触网络安全的学生,我们创建了详细的学习和成长路线图。这可以说是最科学、最系统的学习路线。每个人都可以遵循这个大方向。
路线对应学习视频
同时,还根据成长路线提供了每个部分的支持视频。
2视频配套工具国内外网安书籍、文档
工具
视频
书籍
由于资源比较敏感,所以需要的资源并没有完全显示在底部。
简历模板
由于篇幅有限且信息较为敏感,我们仅展示部分信息。
#关于上述#{}如何防止SQL注入?_sql相关内容来源网络仅供参考。相关信息请参见官方公告。
原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92000.html