Java安全编码之用户输入
0x00 安全引言
1、传统Web应用与新兴移动应用
(1)传统Web应用:浏览器 HTTP 服务器
(2)新兴移动应用:APP HTTP 服务器
从安全角度看,传统Web应用与新兴移动应用没有本质区别
2、Web应用安全的核心问题是什么?
用户提交的数据不可信是Web应用程序核心安全问题
用户可以提交任意输入
例如:
√ 请求参数->多次提交或者不提交
√ 修改Cookie
√ 修改HTTP信息头
√ 请求顺序->跳过或者打乱
3、Web应用防御
(1)完善的异常处理
(2)监控
(3)日志:记录重要业务、异常的详细请求信息
4、对输入的处理
建议采用:白名单
尽量避免:净化或黑名单
0x01 SQL注入
1、原理:
(1)合法输入:
#!sql
id=1
SELECT * FROM users WHRER id='1';
(2)恶意注入:
#!sql
id=1' or '1'='1
SELECT * FROM users WHRER id='1' or 'a'='a';
2、Java代码分析(JDBC)
(1)不合规代码(SQL参数拼接)
#!java
public class SQLInject {
public static void main(String[] args)throws Exception{
//正常输入
select("1");
// 恶意输入
select("' or 'a'='a");
}
public static void select(String id){
//声明Connection对象
Connection con;
//驱动程序名
String driver = "com.mysql.jdbc.Driver";
//URL指向要访问的数据库名mydata
String url = "jdbc:mysql://localhost:3306/mybatis";
//MySQL配置时的用户名
String user = "root";
//MySQL配置时的密码
String password = "budi";
//遍历查询结果集
try {
//加载驱动程序
Class.forName(driver);
//1.getConnection()方法,连接MySQL数据库!!
con = DriverManager.getConnection(url,user,password);
if(!con.isClosed())
System.out.println("Succeeded connecting to the Database!");
//2.创建statement类对象,用来执行SQL语句!!
Statement statement = con.createStatement();
//要执行的SQL语句
String sql = "select * from users where id='"+id+"'";
//3.ResultSet类,用来存放获取的结果集!!
ResultSet rs = statement.executeQuery(sql);
System.out.println("-----------------");
System.out.println("执行结果如下所示:");
System.out.println("-----------------");
String age,name;
while(rs.next()){
//获取stuname这列数据
name = rs.getString("name");
//获取stuid这列数据
age = rs.getString("age");
//输出结果
System.out.println(name + "\t" + age);
}
rs.close();
con.close();
} catch(ClassNotFoundException e) {
//数据库驱动类异常处理
System.out.println("Sorry,can`t find the Driver!");
e.printStackTrace();
} catch(SQLException e) {
//数据库连接失败异常处理
e.printStackTrace();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally{
System.out.println("数据库数据成功获取!!");
}
}
}
执行结果:
#!shell
SQL Paramter:1
-----------------
budi 27
-----------------
SQL Paramter:' or 'a'='a
-----------------
budi 27
budisploit 28
-----------------
(2)合规代码(参数化查询)
#!java
public class SQLFormat {
public static void main(String[] args)throws Exception{
select("1");
select("' or 'a'='a");
}
public static void select(String id){
//声明Connection对象
Connection con;
//驱动程序名
String driver = "com.mysql.jdbc.Driver";
//URL指向要访问的数据库名mydata
String url = "jdbc:mysql://localhost:3306/mybatis";
//MySQL配置时的用户名
String user = "root";
//MySQL配置时的密码
String password = "budi";
//遍历查询结果集
try {
//加载驱动程序
Class.forName(driver);
//1.getConnection()方法,连接MySQL数据库!!
con = DriverManager.getConnection(url,user,password);
if(!con.isClosed())
System.out.println("Succeeded connecting to the Database!");
//2.//要执行的SQL语句
String sql = "select * from users where id=?";
//3.创建statement类对象,ResultSet类,用来存放获取的结果集!!
PreparedStatement stmt = con.prepareStatement(sql);
stmt.setString(1, id);
ResultSet rs = stmt.executeQuery();
System.out.println("-----------------");
System.out.println("执行结果如下所示:");
System.out.println("-----------------");
String age,name;
while(rs.next()){
//获取stuname这列数据
name = rs.getString("name");
//获取stuid这列数据
age = rs.getString("age");
//输出结果
System.out.println(name + "\t" + age);
}
rs.close();
con.close();
} catch(ClassNotFoundException e) {
//数据库驱动类异常处理
System.out.println("Sorry,can`t find the Driver!");
e.printStackTrace();
} catch(SQLException e) {
//数据库连接失败异常处理
e.printStackTrace();
}catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}finally{
System.out.println("数据库数据成功获取!!");
}
}
}
执行结果:
#!shell
SQL Paramter:1
-----------------
budi 27
-----------------
SQL Paramter:' or 'a'='a
-----------------
-----------------
3、防范建议:
√ 采用参数查询即预编译方式(首选)
√ 字符串过滤
0x02 XML注入
1、原理
(1)合法输入:
#!xml
quantity=1
<item>
<name>apple</name>
<price>500.0</price>
<quantity>1</quantity>
<item>
(2)恶意输入:
#!xml
quantity=1</quantity><price>5.0</price><quantity>1
<item>
<name>apple</name>
<price>500.0</price>
<quantity>1</quantity><price>5.0</price><quantity>1</quantity>
<item>
2、Java代码分析
(1)不合规代码(未进行安全检查)
#!java
public class XMLInject2 {
public static void main(String[] args) {
// 正常输入
ArrayList<Map<String, String>> normalList=(ArrayList<Map<String, String>>)
ReadXML("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\inject\\normal.xml","price");
System.out.println(normalList.toString());
// 异常输入
ArrayList<Map<String, String>> evilList=(ArrayList<Map<String, String>>)
ReadXML("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\inject\\evil.xml","price");
System.out.println(evilList.toString());
}
private static List<Map<String,String>> ReadXML(String uri,String NodeName){
try {
//创建一个解析XML的工厂对象
SAXParserFactory parserFactory=SAXParserFactory.newInstance();
//创建一个解析XML的对象
SAXParser parser=parserFactory.newSAXParser();
//创建一个解析助手类
MyHandler myhandler=new MyHandler(NodeName);
parser.parse(uri, myhandler);
return myhandler.getList();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
运行结果:
#!shell
正常输入结果:[{price=500.0}]
恶意输入结果:[{price=500.0}, {price=5.0}]
(2)合规代码(利用schema安全检查)
#!xml
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element name="name" type="xs:string"/>
<xs:element name="price" type="xs:decimal"/>
<xs:element name="quantity" type="xs:integer"/>
</xs:sequence>
</xs:complexType>
</xs:element>
测试代码
#!java
public class XMLFormat{
public static void main(String[] args) {
//测试正常输入
test("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\inject\\normal.xml");
//测试异常输入
test("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\inject\\evil.xml");
}
private static void test(String file) {
SchemaFactory schemaFactory = SchemaFactory
.newInstance("XMLConstants.W3C_XML_SCHEMA_NS_URI");
Schema schema;
try {
schema = schemaFactory.newSchema(new File("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\inject\\schema.xsd"));
Validator validator = schema.newValidator();
validator.setErrorHandler(new ErrorHandler() {
public void warning(SAXParseException exception)
throws SAXException {
System.out.println("警告:" + exception);
}
public void fatalError(SAXParseException exception)
throws SAXException {
System.out.println("致命:" + exception);
}
public void error(SAXParseException exception) throws SAXException {
System.out.println("错误:" + exception);
}
});
validator.validate(new StreamSource(new File(file)));
System.out.println("解析正常");;
} catch (SAXException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("解析异常");
} catch (IOException e) {
// TODO Auto-generated catch block
//e.printStackTrace();
System.out.println("解析异常");
}
}
}
运行结果:
#!shell
正常输入........
解析正常
恶意输入........
错误:org.xml.sax.SAXParseException; systemId: file:/D:/JavaWorkspace/TestInput/src/cn/com/budi/xml/inject/evil.xml; lineNumber: 7; columnNumber: 10; cvc-complex-type.2.4.d: 发现了以元素 'price' 开头的无效内容。此处不应含有子元素。
3、防范建议:
√ 文档类型定义(Document Type Definition,DTD)
√ XML结构化定义文件(XML Schemas Definition)
√ 白名单
0x03 XXE (XML external entity)
1、原理:
(1)合法输入:
#!xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE updateProfile [<!ENTITY lastname "Hello, Budi!">
<!ENTITY file SYSTEM "file:///D:/test.txt">]>
<users >
<firstname>&file</firstname>
<lastname>&lastname;</lastname>
</users>
(2)恶意输入:
#!xml
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE updateProfile [<!ENTITY file SYSTEM "file:///D:/password.txt"> ]>
<users >
<firstname>&file;</firstname>
<lastname>&lastname;</lastname>
</users>
2、Java代码分析
(1)不合规代码(未安全检查外部实体)
#!java
public class XXEInject {
private static void receiveXMLStream(InputStream inStream, MyDefaultHandler defaultHandler) {
// 1.获取基于SAX的解析器的实例
SAXParserFactory factory = SAXParserFactory.newInstance();
// 2.创建一个SAXParser实例
SAXParser saxParser = factory.newSAXParser();
// 3.解析
saxParser.parse(inStream, defaultHandler);
}
public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException{
//正常输入
receiveXMLStream(new FileInputStream("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\xxe\\inject\\normal.xml"),
new MyDefaultHandler());
//恶意输入
receiveXMLStream(new FileInputStream("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\xxe\\inject\\evil.xml"),
new MyDefaultHandler());
}
}
运行结果:
#!shell
正常输入,等待解析......
<firstname>XEE TEST !!</firstname>
==========================
恶意输入,等待解析......
<firstname>OWASP BWA root/owaspbwa
Metasploitable msfadmin/msfadmin
Kali Liunx root/wangpeng
</firstname>
(2)合规代码(安全检查外部实体)
#!java
public class CustomResolver implements EntityResolver{
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException{
//System.out.println("PUBLIC:"+publicId);
//System.out.println("SYSTEM:"+systemId);
System.out.println("引用实体检测....");
String entityPath = "file:///D:/test.txt";
if (systemId.equals(entityPath)){
System.out.println("合法解析:"+systemId);
return new InputSource(entityPath);
}else{
System.out.println("非法实体:"+systemId);
return new InputSource();
}
}
}
测试代码
#!java
public class XXEFormat {
private static void receiveXMLStream(InputStream inStream, MyDefaultHandler defaultHandler) {
// 获取基于SAX的解析器的实例
SAXParserFactory factory = SAXParserFactory.newInstance();
// 创建一个SAXParser实例
SAXParser saxParser;
try {
saxParser = factory.newSAXParser();
//创建读取工具
XMLReader reader = saxParser.getXMLReader();
reader.setEntityResolver(new CustomResolver());
reader.setErrorHandler(defaultHandler);
InputSource is = new InputSource(inStream);
reader.parse(is);
System.out.println("\t成功解析完成!");
} catch (ParserConfigurationException e) {
// TODO Auto-generated catch block
System.out.println("\t非法解析!");
} catch (SAXException e) {
// TODO Auto-generated catch block
System.out.println("\t非法解析!");
} catch (IOException e) {
// TODO Auto-generated catch block
System.out.println("\t非法解析!");
}
}
public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException{
//正常输入
System.out.println("正常输入,等待解析......");
receiveXMLStream(new FileInputStream("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\xxe\\inject\\normal.xml"),
new MyDefaultHandler());
System.out.println("==========================");
//恶意输入
System.out.println("恶意输入,等待解析......");
receiveXMLStream(new FileInputStream("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\xml\\xxe\\inject\\evil.xml"),
new MyDefaultHandler());
}
}
运行结果:
#!shell
正常输入,等待解析......
引用实体检测....
合法解析:file:///D:/test.txt
成功解析完成!
==========================
恶意输入,等待解析......
引用实体检测....
非法实体:file:///D:/password.txt
非法解析!
3、防范建议:
√ 白名单
0x04命令注入
1、原理:
(1)正常输入:
#!shell
dir
(2)恶意输入:
#!shell
dir & ipconfig & net user budi budi /add & net localgroup Administrators admin /add
2、Java代码分析
(1)非合规Window命令注入
#!java
public class OrderWinFault {
public static void main(String[] args) throws Exception{
//正常命令
runOrder("dir");
//恶意命令
runOrder("dir & ipconfig & net user budi budi /add & net localgroup Administrators admin /add");
}
private static void runOrder(String order) throws IOException, InterruptedException{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("cmd.exe /C "+order);
int result = proc.waitFor();
if(result !=0){
System.out.println("process error: "+ result);
}
InputStream in = (result == 0)? proc.getInputStream() : proc.getErrorStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
StringBuffer buffer=new StringBuffer();
String line;
while((line = reader.readLine())!=null){
buffer.append(line+"\n");
}
System.out.print(buffer.toString());
}
}
(2)非合规的Linux注入命令
#!java
public class OrderLinuxFault {
public static void main(String[] args) throws Exception{
// 正常命令
runOrder("ls");
// 恶意命令
runOrder(" ls & ifconfig");
}
private static void runOrder(String order) throws IOException, InterruptedException{
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec(new String [] {"sh", "-c", "ls "+order});
int result = proc.waitFor();
if(result !=0){
System.out.println("process error: "+ result);
}
InputStream in = (result == 0)? proc.getInputStream() : proc.getErrorStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
StringBuffer buffer=new StringBuffer();
String line;
while((line = reader.readLine())!=null){
buffer.append(line+"\n");
}
System.out.print(buffer.toString());
}
}
(3)合规编码(对命令安全检查)
#!java
public class OrderFormat {
public static void main(String[] args) throws Exception{
runOrder("dir");
runOrder("dir & ipconfig & net user budi budi /add & net localgroup Administrators admin /add");
}
private static void runOrder(String order) throws IOException, InterruptedException{
if (!Pattern.matches("[0-9A-Za-z@.]+", order)){
System.out.println("存在非法命令");
return;
}
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("cmd.exe /C "+order);
int result = proc.waitFor();
if(result !=0){
System.out.println("process error: "+ result);
}
InputStream in = (result == 0)? proc.getInputStream() : proc.getErrorStream();
BufferedReader reader=new BufferedReader(new InputStreamReader(in));
StringBuffer buffer=new StringBuffer();
String line;
while((line = reader.readLine())!=null){
buffer.append(line+"\n");
}
System.out.print(buffer.toString());
}
}
3、防范建议:
√ 白名单
√ 严格权限限制
√ 采用命令标号