Как безопасно разобрать строки? - PullRequest
3 голосов
/ 09 октября 2011

Мы знаем, что использование конкатенации строк для формирования запросов SQL делает программу уязвимой для внедрения SQL.Я обычно обхожу это, используя функции параметров, предоставляемые API любого программного обеспечения базы данных, которое я использую.

Но я не слышал, чтобы это было проблемой в обычном системном программировании.Рассмотрим следующий код как часть программы, которая позволяет пользователю записывать файлы только в своем личном каталоге.

Scanner scanner = new Scanner(System.in);
String directoryName = "Bob";
String filePath = null;
String text = "some text";

System.out.print("Enter a file to write to: ");
filePath = scanner.nextLine();

// Write to the file in Bob's personal directory for this program (i.e. Bob/textfile.txt)
FileOutputStream file = new FileOutputStream(directoryName + "/" + filePath);
file.write(text.getBytes());

Является ли вторая-последняя строка уязвимостью?Если да, как сделать программу более безопасной (особенно в Java, C ++ и C #)?Одним из способов является проверка ввода для escape-символов.Что-нибудь еще?

Ответы [ 6 ]

3 голосов
/ 10 октября 2011

Самое простое решение здесь - иметь белый список допустимых символов. Изменение исходного кода (включая соглашения Java, так как вы сказали, что вы новичок ...)

package javawhitelist;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class JavaWhiteListExample {

    public static void main(String[] args) throws IOException {

        Scanner scanner = new Scanner(System.in); 
        String directoryName = "Bob"; 
        String filePath = null; 
        FileWriter stream = null;
        String text = "some text";  
        System.out.print("Enter a file to write to: "); 
        filePath = scanner.nextLine();  
        String WHITELIST = "[^0-9A-Za-z]+";
        Pattern p = Pattern.compile(WHITELIST);
        Matcher m = p.matcher(filePath);

        //You need to do m.find() because m.matches() looks for COMPLETE match
        if(m.find()){ 
            //reject input.
            System.out.println("Invalid input.");
        }else{
            // Write to the file in Bob's home directory (i.e. Bob/textfile.txt) 
            try{
                File toWrite = new File(directoryName + File.separator + filePath);

                if(toWrite.canWrite()){
                    stream = new FileWriter(toWrite);
                    stream.write(text);
                }   
            }catch(FileNotFoundException e){
                e.printStackTrace();
            }catch(IOException e){
                e.printStackTrace();
            }finally{
                if(stream != null){
                    stream.close();
                }
            }

        }
    }
}

Реализация по умолчанию любой JVM выполняется со всеми правами доступа пользователя. Использование метода File.canWrite() поможет гарантировать, что пользователь не будет писать поверх файла, к которому у него нет прав. САМОЕ безопасное решение (с указанием ТОЧНО, куда файл пойдет) будет использовать com.sun.security.auth.module.UnixSystem.getName() и использовать это, чтобы построить /home/$USER часть имени каталога. Некоторые решения могут сказать вам, чтобы использовать System.getProperty("user.home"): или некоторые такие, но те полагаются на легко изменяемые переменные среды.

Я старался быть тщательным, надеюсь, это поможет.

3 голосов
/ 09 октября 2011

Любой ввод от пользователя следует считать " подозрительным "

В вашем случае вы предполагаете, что путь к файлу находится где-то, что пользователь должен написать.

Пользователь может передать любой путь к файлу и изменить (если у программы есть разрешения) файл, который вы не ожидали.

Так что да, строка:

FileOutputStream file = new FileOutputStream(directoryName + "/" + filePath);

действительно уязвимость

Эта концепция также применима к C ++

2 голосов
/ 10 октября 2011

Если вы видите такой код, запустите.

Некоторые проблемы:

Атаки обхода каталога: Традиционно файловые системы путают пользовательский интерфейс и API.У нас есть этот язык с путями к файлам, но мы не можем точно указывать имена.В типичных операционных системах .. позволит перемещаться вверх по структуре каталогов (необязательно в начале пути).Также обратите внимание, что в качестве разделителя каталогов может использоваться более одного символа.

Ссылки: Ссылки файловой системы в каталоге могут ссылаться в другом месте.

NUL-символы: Если вы попытаетесь указать суффикс, тонапример, при расширении файла нулевой байт будет усекать путь.

Оболочки экранированы: могут возникнуть проблемы с кодом оболочки, который пытается интерпретировать путь к файлу, либо до его создания, либо позже.

Существующие файлы: если файл существует, что происходит?

Использование диска: если данные предоставлены пользователем, проверяете ли вы, что они не велики?

Итак, постарайтесь избегатьиспользуя созданные пользователем имена файлов.Если вам действительно нужно, я предлагаю применить жесткий белый список символов.

2 голосов
/ 09 октября 2011

Этот вопрос сильно отличается от проблемы внедрения SQL. В проблеме внедрения SQL, злонамеренно введенные параметры могут использоваться для выполнения команд в привилегированном контексте безопасности, поскольку пользователь database , под которым выполняются команды, обычно имеет карт-бланш-доступ для записи в строки в базе данных.

В приведенном вами примере ключевой вопрос: «каким пользователем будет выполняться код Java?». Если вы выполняете этот код как, например, скрипт CGI, то любой файл или каталог, в который пользователь процесса веб-сервера может записать, уязвим. Если вы просто запускаете это из командной строки, это действительно зависит от операционной системы (а не кода Java) для защиты файлов / каталогов, в которые пользователь не должен иметь возможность записи.

Если ваше намерение состоит в том, чтобы разрешить запись кода только в каталог пользователя, то остальные ответы верны. Однако я могу представить множество сценариев, в которых это может быть не так. например Возможно, вы пишете некоторый код для автоматического редактирования файла в каталоге / etc.

Короче говоря, вы хотите рассмотреть контекст, в котором будет выполняться ваш код, и какую безопасность будет обеспечивать этот контекст, и какую безопасность вам нужно будет обеспечить в вашем собственном коде в этом контексте.

PS - Вы обычно не хотите предполагать, что "/" является вашим разделителем каталогов. Java предоставляет для этой цели константу File.separator .

2 голосов
/ 09 октября 2011

Поскольку в имени файла есть несколько зарезервированных символов , возможно, вы захотите найти путь, указанный пользователем.Вы также можете проверить, что строка не содержит ../, :/ и т. Д., Что позволит пользователю вмешиваться в путь «home directory».Я бы рекомендовал использовать регулярное выражение для проверки заданной строки перед ее использованием.Если проверка не удалась, просто прервите операцию и дайте пользователю знать, что с вводом что-то не так, вместо того, чтобы пытаться это исправить.

Структура файла может быть довольно сложной, если человек не знает, что он делает, и символы - не единственная проблема, как упоминалось в других ответах.Какие допустимые имена файлов различны в разных файловых системах.Старые FAT-системы имеют ограничение не более 8 символов, в то время как более новые NTFS, используемые Windows, допускают до 255 символов.

Обновленный ответ для большей ясности.

1 голос
/ 09 октября 2011

Вы можете получить каталог пользователя с System.getProperty("user.home").Если ваша программа работает под этим пользователем, и права пользователей управляются правильно, проблем не ожидается.Также вы можете получить символ разделителя пути с другим свойством - file.separator.И, наконец, есть методы File.canRead() и File.canWrite().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...