<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/15129

            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、防范建議:

            √ 白名單
            √ 嚴格權限限制
            √ 采用命令標號

            0x05 壓縮炸彈(zip bomb)


            (1)合法輸入:

            #!shell
            普通壓縮比文件normal.zip
            

            (2)惡意輸入:

            #!shell
            高壓縮比文件evil.zip
            

            2、Java代碼分析

            #!java
            public class ZipFault {
                static final  int BUFFER = 512;
                public static void main(String[] args) throws IOException{
                    System.out.println("正常壓縮文件.......");
                    checkzip("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\zip\\normal.zip");
                    System.out.println("惡意壓縮文件.......");
                    checkzip("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\zip\\evil.zip");
                }
                private static void checkzip(String filename) throws IOException{
                    BufferedOutputStream dest = null;
                    FileInputStream fls = new FileInputStream(filename);
                    ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fls));
                    ZipEntry entry;
                    long begin = System.currentTimeMillis();   
                    while ((entry = zis.getNextEntry()) != null){
                        System.out.println("Extracting:" + entry+"\t解壓后大小:"+entry.getSize());
                        int count;
                        byte data[] = new byte[BUFFER];
                        FileOutputStream fos = new FileOutputStream("D:/"+entry.getName());
                        dest = new BufferedOutputStream(fos, BUFFER);
                        while ((count = zis.read(data, 0, BUFFER))!=-1){
                            dest.write(data,0, count);
                        }
                        dest.flush();
                        dest.close();
                    }
                    zis.close();
                    long end = System.currentTimeMillis();   
                    System.out.println("解壓縮執行耗時:" + (end - begin) + " 豪秒");
                }
            }
            

            運行結果:

            #!shell
            正常壓縮文件.......
            Extracting:normal.txt   解壓后大小:17496386
            解壓縮執行耗時:382 豪秒
            惡意壓縮文件.......
            Extracting:evil.txt 解壓后大小:2000000000
            解壓縮執行耗時:25911 豪秒
            

            (2)合規代碼

            #!java
            public class ZipFormat {
                static final  int BUFFER = 512;
                static final int TOOBIG = 0x640000;
                public static void main(String[] args) throws IOException{
                    checkzip("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\zip\\normal.zip");
                    checkzip("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\zip\\evil.zip");
                }
                private static void checkzip(String filename) throws IOException{
                    BufferedOutputStream dest = null;
                    FileInputStream fls = new FileInputStream(filename);
                    ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fls));
                    ZipEntry entry;
                    long begin = System.currentTimeMillis();   
                    while ((entry = zis.getNextEntry()) != null){
                        System.out.println("Extracting:" + entry+"\t解壓后大小:"+entry.getSize());
                        if (entry.getSize() > TOOBIG){
                            System.out.println("壓縮文件過大");
                            break;
                        }
                        if (entry.getSize() == -1){
                            System.out.println("文件大小異常");
                        }
                        int count;
                        byte data[] = new byte[BUFFER];
                        FileOutputStream fos = new FileOutputStream("D:/"+entry.getName());
                        dest = new BufferedOutputStream(fos, BUFFER);
                        while ((count = zis.read(data, 0, BUFFER))!=-1){
                            dest.write(data,0, count);
                        }
                        dest.flush();
                        dest.close();
                    }
                    zis.close();
                    long end = System.currentTimeMillis();   
                    System.out.println("解壓縮執行耗時:" + (end - begin) + " 豪秒");
                }
            }
            

            運行結果:

            #!shell
            正常文件.........
            Extracting:normal.txt   解壓后大小:17496386
            解壓縮執行耗時:378 豪秒
            ===================
            惡意文件.........
            Extracting:evil.txt 解壓后大小:2000000000
            壓縮文件過大
            解壓縮執行耗時:0 豪秒
            

            3、防范建議:

            √ 解壓前檢查解壓后文件大小

            0x06 正則表達式注入


            1、原理:

            (1)合法輸入

            #!shell
            search=error
            

            拼接后

            #!shell
            (.*? +public\\[\\d+\\]+.*error.*)
            

            (2)惡意輸入

            #!shell
            search=.*)|(.*
            

            拼接后

            #!shell
            (.*? +public\\[\\d+\\]+.*.*)|(.*.*)
            

            2、Java代碼分析

            (1)非合規代碼(未進行安全檢查)

            #!java
            public class RegexFault {
                /**
                 * 以行為單位讀取文件,常用于讀面向行的格式化文件
                 */
                public static void readFileByLines(String filename,String search) {
                    File file = new File(filename);
                    BufferedReader reader = null;
                    String regex ="(.*? +public\\[\\d+\\] +.*"+search+".*)";
                    System.out.println("正則表達式:"+regex);
                    try {
                        reader = new BufferedReader(new FileReader(file));
                        String tempString = null;
                        int line = 1;
                        System.out.println("查找開始......");
                        // 一次讀入一行,直到讀入null為文件結束
                        while ((tempString = reader.readLine()) != null) {
                            //System.out.println("line " + line + ": " + tempString);
                            if(Pattern.matches(regex, tempString)){
                                // 顯示行號
                                System.out.println("line " + line + ": " + tempString);
                            }
                            line++;
                        }
                        reader.close();
                        System.out.println("查找結束....");
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (reader != null) {
                            try {
                                reader.close();
                            } catch (IOException e1) {
                            }
                        }
                    }
                }
                public static void   main(String[] args){
                    //正常輸入
                    readFileByLines("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\regex\\regex.log","error");
                    //惡意輸入
                   readFileByLines("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\regex\\regex.log",".*)|(.*");
                }
            }
            

            運行結果:

            #!shell
            正常輸入......
            正則表達式:(.*? +public\[\d+\] +.*error.*)
            line 5: 10:48:08 public[48964] Backup failed with error: 19
            ============================
            惡意輸入......
            正則表達式:(.*? +public\[\d+\] +.*.*)|(.*.*)
            line 1: 10:47:03 private[423] Successful logout name: budi ssn: 111223333
            line 2: 10:47:04 public[48964] Failed to resolve network service
            line 3: 10:47:04 public[1] (public.message[49367]) Exited with exit code: 255
            line 4: 10:47:43 private[423] Successful login name: budisploit ssn: 444556666
            line 5: 10:48:08 public[48964] Backup failed with error: 19
            

            (2)合規代碼(進行安全檢查)

            #!java
            public class RegexFormat {
                /**
                 * 檢測是否存在非法字符
                 * @param search
                 */
                private static boolean validate(String search){
                    for (int i = 0; i< search.length(); i++){
                        char ch = search.charAt(i);
                        if(!(Character.isLetterOrDigit(ch) || ch ==' ' || ch =='\'')){
                            System.out.println("存在非法字符,查找失敗....");
                            return false;
                        }
                    }
                    return true;
                }
                /**
                 * 以行為單位讀取文件,常用于讀面向行的格式化文件
                 */
                public static void readFileByLines(String filename,String search) {
                    if(!validate(search)){
                        return;
                    }
                    File file = new File(filename);
                    BufferedReader reader = null;
                    String regex ="(.*? +public\\[\\d+\\] +.*"+search+".*)";
                    System.out.println("正則表達式:"+regex);
                    try {
                        reader = new BufferedReader(new FileReader(file));
                        String tempString = null;
                        int line = 1;
                        System.out.println("查找開始......");
                        // 一次讀入一行,直到讀入null為文件結束
                        while ((tempString = reader.readLine()) != null) {
                            //System.out.println("line " + line + ": " + tempString);
                            if(Pattern.matches(regex, tempString)){
                                // 顯示行號
                                System.out.println("line " + line + ": " + tempString);
                            }
                            line++;
                        }
                        reader.close();
                        System.out.println("查找結束....");
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        if (reader != null) {
                            try {
                                reader.close();
                            } catch (IOException e1) {
                            }
                        }
                    }
                }
                public static void   main(String[] args){
                    //正常輸入
                    System.out.println("正常輸入......");
                    readFileByLines("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\regex\\regex.log","error");
                    System.out.println("============================");
                    //惡意輸入
                    System.out.println("惡意輸入......");
                   readFileByLines("D:\\JavaWorkspace\\TestInput\\src\\cn\\com\\budi\\regex\\regex.log",".*)|(.*");
                }
            }
            

            運行結果:

            #!shell
            ============================
            正常輸入......
            正則表達式:(.*? +public\[\d+\] +.*error.*)
            line 5: 10:48:08 public[48964] Backup failed with error: 19
            ============================
            惡意輸入......
            存在非法字符,查找失敗....
            

            3、防范建議:

            √ 白名單

            0x07 未凈化輸入


            (1)日志記錄

            正常輸入:

            #!shell
            budi
            

            日志記錄:

            #!shell
            User Login Successed for: budi
            

            惡意輸入:

            #!shell
            budi \nUser Login Successed for: administrator
            

            日志記錄:

            #!shell
            User Login Failed for: budi 
            User Login Successed for: administrator
            

            (2)更新用戶名

            正常輸入:

            #!sql
            username=budi
            

            SQL查詢:

            #!sql
            SELECT * FROM users WHRER id='budi';
            

            惡意輸入:

            #!sql
            username=budi' or 'a'='a
            

            SQL查詢:

            #!sql
            SELECT * FROM users WHRER id='budi' or 'a'='a';
            

            2、Java代碼分析

            (1)非合規代碼(未安全檢查)

            #!java
            public class LogFault {
                private static void writeLog( boolean isLogin,String username){
                    if(isLogin){
                        System.out.println("User Login Successed for: "+username);
                    }else{
                        System.out.println("User Login Failed for: "+username);
                    }
                }
                public static void main(String[] args){
                    String test1= "budi";
                    System.out.println("正常用戶登錄成功后,記錄日志.....");
                    //正常用戶登錄成功后,記錄日志
                    writeLog(true, test1);
                    //惡意用戶登錄失敗,記錄日志
                    String test2 = "budi \nUser Login Successed for: administrator";
                    System.out.println("惡意用戶登錄失敗,記錄日志.....");
                    writeLog(false, test2);
                }
            }
            

            運行結果:

            #!shell
            正常用戶登錄成功后,記錄日志.....
            User Login Successed for: budi
            惡意用戶登錄失敗,記錄日志.....
            User Login Failed for: budi 
            User Login Successed for: administrator
            

            (2)合規代碼(安全檢查)

            #!java
            public class LoginFormat {
                private static void writeLog( boolean isLogin,String username){
                    if(!Pattern.matches("[A-Za-z0-9_]+", username)){
                        System.out.println("User Login Failed for Unknow User");
                    }else   if(isLogin){
                        System.out.println("User Login Successed for: "+username);
                    }else{
                        System.out.println("User Login Failed for: "+username);
                    }
                }
                public static void main(String[] args){
                    String test1= "budi";
                    System.out.println("正常用戶登錄成功后,記錄日志.....");
                    writeLog(true, test1);
                    String test2 = "budi \nUser Login Successed for: administrator";
                    System.out.println("惡意用戶登錄失敗,記錄日志.....");
                    writeLog(false, test2);
                }
            }
            

            運行結果:

            #!shell
            正常用戶登錄成功后,記錄日志.....
            User Login Successed for: budi
            惡意用戶登錄失敗,記錄日志.....
            User Login Failed for Unknow User
            

            3、防范建議:

            √ 先檢測用戶輸入,強烈建議直接拒絕帶非法字符的數據

            0x08 路徑遍歷


             1、原理:

            (1)正常輸入:

            #!shell
            john.txt
            

            (2)惡意輸入:

            #!shell
            ../../a.txt"
            

            2、Java代碼分析

            (1)非合規代碼(未安全檢查)

            #!java
            public class PathFault {
                public static void main(String[] args) throws IOException{
                    System.out.println("合法輸入.......");
                    readFile("john.txt");
                    System.out.println("\n惡意輸入.......");
                    readFile("../../a.txt");
                }
                private static void readFile(String path) throws IOException{
                    File f = new File("F://passwords//"+path);
                    String absPath = f.getAbsolutePath();
                    FileOutputStream fls = new FileOutputStream(f);
                    System.out.print("絕對路徑:"+absPath);
                    if(!isInSecureDir(Paths.get(absPath))){
                        System.out.println("->非安全路徑");
                        return;
                    }
                    System.out.print("->安全路徑");
                }
                private static boolean isInSecureDir(Path path){
                    if(!path.startsWith("F://passwords//")){
                        return false;
                    };
                    return true;
                }
            }
            

            運行結果:

            #!shell
            合法輸入.......
            絕對路徑:F:\passwords\john.txt->安全路徑
            惡意輸入.......
            絕對路徑:F:\passwords\..\..\a.txt->安全路徑
            

            (2)合規代碼(先統一路徑表示)

            #!java
            public class PathFormat {
                public static void main(String[] args) throws IOException{
                    System.out.println("合法輸入.......");
                    readFile("john.txt");
                    System.out.println("/n惡意輸入.......");
                    readFile("../../a.txt");
                }
                private static void readFile(String path) throws IOException{
                    File f = new File("F://passwords//"+path);
                    String canonicalPath = f.getCanonicalPath();
                    System.out.println("絕對路徑"+canonicalPath);
                    FileInputStream fls = new FileInputStream(f);
                    if(!isInSecureDir(Paths.get(canonicalPath))){
                        System.out.print("非安全路徑");
                        return;
                    }
                    System.out.print("安全路徑");
                }
                private static boolean isInSecureDir(Path path){
                    if(!path.startsWith("F://passwords//")){
                        return false;
                    };
                    return true;
                }
            }
            

            運行結果:

            #!shell
            合法輸入.......
            絕對路徑F:\passwords\john.txt->安全路徑
            惡意輸入.......
            絕對路徑F:\a.txt->非安全路徑
            

            3、防范建議

            √ 嚴格的權限限制->安全管理器
            √ getCanonicalPath()在所有平臺上對所有別名、快捷方式、符號鏈接采用統一的解析。

            0x09 格式化字符串


            1、原理:

            (1)正常輸入:

            11

            正常拼接:

            #!shell
            System.out.printf("11 did not match! HINT: It was issued on %1$te rd of some month\n", c);
            

            (2)惡意輸入:

            #!shell
            %1$tm或%1$te或%1$tY
            

            惡意拼接:

            #!shell
            System.out.printf("%1$tm did not match! HINT: It was issued on %1$te rd of some month\n", c);
            

            2、Java代碼分析

            (1)非合規代碼:

            #!java
            public class DateFault {
                static Calendar c = new GregorianCalendar(2016, GregorianCalendar.MAY, 23);
                public static void main(String[] args){
                    //正常用戶輸入
                    System.out.println("正常用戶輸入.....");
                    format("11");
                    System.out.println("非正常輸入獲取月份.....");
                    format("%1$tm");
                    System.out.println("非正常輸入獲取日.....");
                    format("%1$te");
                    System.out.println("非正常輸入獲取年份.....");
                    format("%1$tY");
                }
                private static void format(String month){
                    System.out.printf(month+" did not match! HINT: It was issued on %1$te rd of some month\n", c);
                }
            }
            

            運行結果:

            #!shell
            11 did not match! HINT: It was issued on 23rd of some month
            非正常輸入獲取月份.....
            05 did not match! HINT: It was issued on 23rd of some month
            非正常輸入獲取日.....
            23 did not match! HINT: It was issued on 23rd of some month
            非正常輸入獲取年份.....
            2016 did not match! HINT: It was issued on 23rd of some month
            

            (2)合規代碼:

            #!java
            public class DateFormat {
                static Calendar c = new GregorianCalendar(2016, GregorianCalendar.MAY, 23);
                public static void main(String[] args){
                    //正常用戶輸入
                    System.out.println("正常用戶輸入.....");
                    format("11");
                    System.out.println("非正常輸入獲取月份.....");
                    format("%1$tm");
                    System.out.println("非正常輸入獲取日.....");
                    format("%1$te");
                    System.out.println("非正常輸入獲取年份.....");
                    format("%1$tY");
                }
                private static void format(String month){
                    System.out.printf("%s did not match! HINT: It was issued on %1$te rd of some month\n", month, c);
                }
            }
            

            運行結果:

            #!shell
            正常用戶輸入.....
            11 did not match! HINT: It was issued on 
                                  Exception in thread "main" java.util.IllegalFormatConversionException: e != java.lang.String
            

            3、防范建議:

            √ 對用戶輸入進行安全檢查
            √ 在格式字符串中,杜絕使用用戶輸入參數

            0x0A 字符串標準化


            1、原理:

            (1)合法輸入:

            #!shell
            username=budi
            

            (2)惡意輸入一:

            #!shell
            username=/><script>alert(1)</script>
            username=/\uFE65\uFE64script\uFE65alert(1) \uFE64/script\uFE65
            

            (3)惡意輸入二:

            #!shell
            username=A\uD8AB
            username=A?
            

            2、Java代碼分析

            (1)非合規代碼(先檢查再統一編碼)

            #!java
            public class EncodeFault {
                public static void main(String[] args){
                    System.out.println("未編碼的非法字符");
                    check("/><script>alert(2)</script>");
                    System.out.println("Unicode編碼的非法字符");
                    check("/\uFE65\uFE64script\uFE65alert(1) \uFE64/script\uFE65");
                }
                public static void check(String s){
                    Pattern pattern = Pattern.compile("[<>]");
                    Matcher matcher = pattern.matcher(s);
                    if (matcher.find()){
                        System.out.println(s+"->存在非法字符");
                    }else{
                        System.out.println(s+"->合法字符");
                    }
                    s = Normalizer.normalize(s, Form.NFC);
                }
            }
            

            運行結果:

            #!shell
            未編碼的非法字符
            /><script>alert(2)</script>->存在非法字符
            Unicode編碼的非法字符
            /﹥﹤script﹥alert(1) ﹤/script﹥->合法字符
            

            (3)合規代碼(先統一編碼再檢查)

            #!java
            public class EncodeFormat {
                public static void main(String[] args){
                    System.out.println("未編碼的非法字符");
                    check("/><script>alert(2)</script>");
                    System.out.println("Unicode編碼的非法字符");
                    check("/\uFE65\uFE64script\uFE65alert(1)\uFE64/script\uFE65");
                }
                public static void check(String s){
                    s = Normalizer.normalize(s, Form.NFC);
                    // 用\uFFFD替代非Unicode編碼字符
                    s = s.replaceAll("^\\p{ASCII}]", "\uFFFD");
                    Pattern pattern = Pattern.compile("[<>]");
                    Matcher matcher = pattern.matcher(s);
                    if (matcher.find()){
                        System.out.println(s+"->存在非法字符");
                    }else{
                        System.out.println(s+"->合法字符");
                    }
                }
            }
            

            運行結果:

            #!shell
            未編碼的非法字符
            /><script>alert(2)</script>->存在非法字符
            Unicode編碼的非法字符
            /><script>alert(1)</script>->存在非法字符
            

            3、防范建議:

            √ 先按指定編碼方式標準化字符串,再檢查非法輸入
            √ 檢測非法字符

            0x0B 最后總結


            參考文獻: 《Java安全編碼標準》

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线