Задать вопрос
  • Допускается ли возможность заражения машины сугубо открытием ссылки на сайт?

    Vamp
    @Vamp
    С лёгкостью заразиться не получится, потому что web стал слишком важной инфраструктурой, поэтому компании прилагают много усилий для его развития. В том числе в плане безопасности. Но софт пишут люди, а люди неспособны производить безошибочный код. Поэтому ошибки всегда будут, а значит и уязвимости, позволяющие обходить изолированную песочницу.

    Фактов полно в интернете. Например, этот:

    According to the screenshot shared by Agarwal, the PoC HTML file, and its associated JavaScript file, can be loaded in a Chromium-based browser to exploit the security flaw and launch the Windows calculator (calc.exe) app. But it's worth noting that the exploit needs to be chained with another flaw that can allow it to escape Chrome's sandbox protections.

    Или такой пример. Джеилбрейк iOS через уязвимость в браузере Safari.
    Ответ написан
    Комментировать
  • Как средствами PHP нарисовать дугу для SVG файла?

    Vamp
    @Vamp
    Проще всего нарисовать дугу через элемент path.

    $image = new SVG('100mm','100mm');
    $doc = $image->getDocument();
    $square = new SVGLine('0mm', '0mm', '55mm', '55mm'); // x0 y0 x1 y1
    $square->setStyle('stroke', '#FF0000'); //цвет
    $doc->addChild($square);
    
    $arc = new SVGPath('m 40,40 A 30,30,0,0,1,150,150');
    $arc->setStyle('stroke', '#FF0000');
    $doc->addChild($arc);
    
    header('Content-Type: image/svg+xml');
    echo $image;


    Нужно немного изучить формат path. Он не сложный.

    m 40,40 A 30,30,0,0,1,150,150

    m - это команда move. Устанавливает "кисть" в указанную позицию. Далее команда A - arc (дуга). Её формат несколько сложнее:

    30,30 - радиус дуги по осям x и y
    0 - угол поворота дуги вокруг оси x
    0 - должна ли дуга быть больше 180 градусов или нет
    1 - в какую сторону рисовать дугу - по часовой или против
    150,150 - координаты конца дуги
    Ответ написан
    2 комментария
  • Возможно ли выбрать российский сервер в CloudFlare?

    Vamp
    @Vamp
    Нет, вы не можете в CloudFlare выбирать местоположение. Даже сам CloudFlare не может.

    CloudFlare использует технологию anycast. В двух словах это когда один и тот же IP адрес анонсируется сразу несколькими географически разнесёнными дата центрами. И для посетителя из америки трафик будет приземляться на американский ДЦ, а для посетителя из россии на российский ДЦ даже если оба таких поселителя заходят на один и тот же IP адрес.

    Поисковики в курсе этой технологии, так как сами ею пользуются. Так что география сайта не определяется ими исключительно по IP адресу.
    Ответ написан
    2 комментария
  • Как собирать метрики с самого сервера через Prometheus?

    Vamp
    @Vamp
    Вам нужно установить на хост node_exporter (можно в докере). Он будет собирать метрики с хоста и экспортировать их для prometheus.
    Ответ написан
    Комментировать
  • Как проверить какая ошибка в $_FILES?

    Vamp
    @Vamp
    Согласно официальной документации, превышение post_max_size можно отследить только добавив get параметр к форме:
    <form action="edit.php?processed=1">

    if (!empty($_GET['processed']) && empty($_POST) && empty($_FILES)) {
        echo 'Ошибка! Вы загрузили слишком большой файл';
    }
    Ответ написан
  • Что будет за раскрытие факта уязвимости на гос-ресурсе?

    Vamp
    @Vamp
    Напишите обращение в Роскомнадзор. Персональные данные - это их вотчина.

    Мол, данный сайт раздает персональные данные направо и налево, прошу провести правовую оценку и принять меры.

    Не пишите про взлом или ещё что-то. Иначе это повод для ст. 272 УК РФ. Плохо, что вы угрожали публикацией в СМИ. Это тоже могут использовать против вас.
    Ответ написан
    Комментировать
  • Можно ли получать данные из ResultSet не удаляя их из ResultSet?

    Vamp
    @Vamp
    Можно "перемотать" ResultSet и повторно прочитать из него данные:
    try (ResultSet rs = statement.executeQuery(query)) {
        while (rs.next()) {
            System.out.println("User: " + rs.getString("login"));
        }
        rs.beforeFirst(); // <-- перематываем
        while (rs.next()) {
            System.out.println("Hello, " + rs.getString("login"));
        }
    }


    Но это не сработает, если драйвер создал ResultSet типа TYPE_FORWARD_ONLY или реализация ResultSet не поддерживает перемотку в принципе. В этом случае придётся прочитать весь результат полностью в промежуточное хранилище и дальше работать уже с ним:
    class User {
        private final String login;
        private final String name;
        private final String email;
    
        public User(ResultSet rs) throws SQLException {
            login = rs.getString("login");
            name = rs.getString("name");
            email = rs.getString("email");
        }
    
        public String getLogin() {
            return login;
        }
        public String getName() {
            return name;
        }
        public String getEmail() {
            return email;
        }
    }

    List<User> users = new ArrayList<>();
    try (ResultSet rs = statement.executeQuery(query)) {
        while (rs.next()) {
            users.add(new User(rs));
        }
    }
    for (User u : users) {
        System.out.println("Hello, " + u.getLogin());
    }

    Вариант с использованием отдельного класса для хранения результатов используется повсеместно и имеет своё собственное название - DTO (Data Transfer Object).
    Ответ написан
    Комментировать
  • Nginx, странно работает deny all. Что делать?

    Vamp
    @Vamp
    У обычного префиксного location приоритет ниже, чем у location с регулярным выражением.

    Повысьте приоритет запрещающего правила модификатором ^~:
    location ^~ /engine/ {
        deny all;
    }
    Ответ написан
    Комментировать
  • Почему поток не отрабатывает задуманного в Java?

    Vamp
    @Vamp
    Такой вывод получается из-за того, что тест считается завершенным сразу после выхода из метода runWithSleep, после которого программа завершается принудительно, несмотря на наличие всё ещё выполняющихся не daemon потоков.

    Чтобы увидеть полный вывод запустите код не в контексте junit или добавьте ожидание в код теста:

    exec.shutdown();
    exec.awaitTermination(100, TimeUnit.SECONDS);
    Ответ написан
    2 комментария
  • Php Comet + Sokil\Mongo, почему class '\MongoCollection' not found, хотя mongodb на php поставлен?

    Vamp
    @Vamp
    Необходимо установить расширение mongodb для самого PHP и дополнительную библиотеку alcaeus/mongo-php-adapter.
    Ответ написан
  • Как правильно организовать обслуживание и работу с большой БД?

    Vamp
    @Vamp
    Касательно mysql знаю три варианта:

    1. Утилита pt-online-schema-change. Создаёт пустую копию исходной таблицы, делает на ней alter, копирует данные из исходной таблицы и в конце меняет местами старую и новую таблицы.

    Пользовался этой утилитой пару раз. Хорошо работает.

    2. В mysql 5.6 появилась возможность делать alter без блокировок средствами самой субд. Нужно в alter добавить парочку новых параметров:
    ALTER TABLE tbl_name ADD PRIMARY KEY (column), ALGORITHM=INPLACE, LOCK=NONE;
    Этот вариант я сам не пробовал, поэтому прокомментировать не могу.

    3. Самый сложный вариант с использованием двух инстансов mysql, связанных master-master репликацией:

    1. Делаем alter на втором сервере
    2. Ждём, когда второй сервер догонится по репликации
    3. Переключаем сервис на вторую базу
    4. Ждем какое-то время, смотрим, нормально ли приложение работает с новой схемой, нет ли деградации или ошибок
    5. Делаем alter на первой базе
    6. Ждём догона репликации
    7. Возвращаем сервис на первую базу

    С этим вариантом я работаю постоянно. Выглядит просто, но на деле много нюансов.

    Нужно обязательно пропускать alter мимо репликации:
    SET sql_log_bin = 0;
    ALTER TABLE tbl_name ...;
    Важно не забыть про sql_log_bin = 0, иначе alter по репликации переедет на соседний сервер и залочит таблицу уже там. А переключать сервис нельзя, пока репликация не догонится.

    Если меняется структура таблицы - добавляется/удаляется колонка или меняется их порядок, нужно обязательно проследить чтобы тип репликации обязательно был STATEMENT. Иначе репликация приляжет на первом же запросе в формате ROW с примерно такой странной ошибкой:

    Column 25 of table 'mydb.mytable' cannot be converted from type 'varchar(255)' to type 'bigint(20) unsigned'


    А со STATEMENT нужно следить чтобы приложение нигде не понижало уровень изоляции ниже REPEATABLE READ, иначе получит ошибку:

    Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT and at least one table uses a storage engine limited to row-based logging. InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.
    Ответ написан
    Комментировать
  • Зачем указывают ssl_dhparam в конфиге nginx? Какой риск если его убрать из конфига?

    Vamp
    @Vamp
    dhparam - это простое число, используемое в алгоритме Диффи-Хеллмана для обмена сессионными ключами с клиентом.

    Указание ssl_dhparam делает доступным для использования в nginx семейство алгоритмов DHE/EDH. Как раз тех, что используют Forward Secrecy, о которых пишется в вашей цитате. Если в двух словах, то при использовании алгоритмов с FS злоумышленник не сможет расшифровать перехваченный трафик, даже если завладеет приватным ключом сервера.

    Маленькое значение этого простого числа делает возможным атаку logjam на TLS, поэтому следует генерировать большое. 4096 бит будет достаточно с запасом.

    Команда для генерации файла dhparam:
    openssl dhparam -out /etc/pki/nginx/dhparam.pem 4096
    Ответ написан
    5 комментариев
  • Почему при отправке сообщений в Кафке, если не правильно написан топик которого не существует то сообщения все равно уходят и нет ошибки?

    Vamp
    @Vamp
    Нужно в настройках самого кластера кафки установить параметр auto.create.topics.enable в значение false
    Ответ написан
    Комментировать
  • Как организовать переадресацию с cas сервера?

    Vamp
    @Vamp
    При переадресации на cas вам необходимо передать параметрами куда редиректить пользователя обратно.

    Если взять этот сайт для примера (qna.habr.com), то при попытке авторизации перекидывает на
    https://account.habr.com/login/?consumer=qna&ostate=bb9acafa489dc93106685c101891cfffb


    Обратите внимание на параметр consumer. По нему cas определяет куда возвращать пользователя. При авторизации на хабре в этом параметре будет habr. В хабр карьере будет career. И так далее.

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

    Либо можете прям сразу конкретную ссылку передать:
    https://cas.example.org/?return_to=https%3A%2F%2Fexample.com%2Farticles%3Fpage%3D8

    Но в таком случае на стороне cas сервера нужно дополнительно валидировать адрес, чтобы защитить пользователей от возможного фишинга.
    Ответ написан
  • Можно ли с помощью Kafka объединять результаты задач на базе общего id?

    Vamp
    @Vamp
    Вашу задачу в такой постановке вполне можно решить. Вот только склеиванием результатов придётся заниматься вручную.

    Создайте топик с результатами и в качестве ключа возьмите order_id. Далее считывайте результаты из топика и складывайте в коллекцию Map<Integer, Set<TaskResult>> (где Integer - order_id). Как только количество элементов в Set станет равным количеству ранее отправленных задач по данному order_id - можно считать, что все ответы получены и передавать их все разом на дальнейшую обработку.

    Останется только продумать крайние случаи. Например, нельзя до бесконечности ждать поступления всех результатов - external api может не ответить, а локальная задача вылететь с эксепшеном и не сгенерировать TaskResult. В этом случае количество ответов будет меньше количества отправленных задач. Придется прикручивать таймауты и/или отправлять задачи повторно. А что делать если вдруг ответов поступит больше, чем отправлялось запросов?

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

    Можно создавать отдельный топик под каждый order. Здесь упрощается обработка некоторых corner кейсов, но возникает проблема если order'ов много (сотни тысяч - миллионы).

    С kafka streams не работал, но бегло пробежав по документации, могу предположить, что комбинация groupByKey() + reduce() может решить вопрос меньшим количеством кода, чем у предыдущих двух вариантов.
    Ответ написан
    2 комментария
  • Apache Kafka как сделать управляющий канал?

    Vamp
    @Vamp
    Если хотите обойтись одним топиком, то делайте количество партиций по количеству агентов и подписывайте каждого агента на свою партицию.
    Ответ написан
  • Как послать сигнал на дочерний sh процесс?

    Vamp
    @Vamp
    Существует возможность заменить процесс sh процессом java при помощи команды exec:
    sh -c "exec java -jar ol.jar"

    В этом случае дочерний процесс займёт место родителя. В частности, получит PID bash процесса, который вам уже известен. Разумеется, если в скрипте после вызова java идут какие-то ещё команды, то они никогда не выполнятся, так как процесс bash перестаёт существовать в момент вызова exec.

    Если всё же необходимо выполнить какие-нибудь завершающие команды по окончании работы java процесса, то существует возможность повесить собственные обработчики сигналов:
    #!/bin/sh
    
    # Устанавливаем обработчик сигналов HUP INT QUIT TERM и псевдосигнала EXIT.
    # В обработчике отправляем сигнал TERM процессу, чей pid будет записан в
    # переменной CHILDPID.
    trap 'kill $CHILDPID' HUP INT QUIT TERM EXIT
    
    # Запускаем java в фоне.
    java -jar ol.jar &
    
    # Сохраняем PID только что запущенного дочернего java процесса.
    CHILDPID=$!
    
    # Следующая команда ставит процесс sh на паузу.
    # Пауза снимется после завершения фонового java процесса.
    # Обработчик сигналов при этом остаётся работоспособным и будет
    # корректно отрабатывать во время паузы.
    wait
    
    # В этом месте java уже завершилась и можно что-нибудь сделать.
    # Например, подчистить временные файлы.
    echo Cleanup actions
    Ответ написан
    Комментировать
  • Как реализовать "защиту" авторизации по номеру телефона?

    Vamp
    @Vamp
    1. Проверьте синтаксическую корректность номера телефона. Все мобильные номера в РФ начинаются на +79 и имеют длину ровно 11 цифр. Проверку можно даже добавить в веб форму на уровне js. Это нельзя назвать защитой от хулиганов, но она отсеет реальные ошибки и опечатки, облегчив жизнь обычным пользователям.

    2. Пробейте номер по базе россвязи (файл DEF-9xx). Так вы определите номера, на которые 100% не будет доставки. В отличии от проверки синтаксиса, не выдавайте пользователю ответ о некорректном номере. На все номера отвечайте "Одноразовый код отправлен, введите его сюда", но на невалидные номера не отправляйте сообщение.

    3. Добавьте ограничение на количество отправляемых форм в минуту с одного IP и количество отправляемых сообщений на один и тот же номер (независимо от IP).

    4. Оцените сколько может быть отправок форм в день и поставьте общий лимит на все отправки смс за день. Это единственный реальный способ контролировать атаку на сливание бюджета. Да, реальные пользователи пострадают при достижении лимита, но вы не должны доводить до него - настройте мониторинг количества отправляемых смс и алертинг при достижении порога в 90% от лимита, чтобы у вас было время среагировать на атаку и отбить её до полного исчерпания лимита. Либо если это всплеск реальных пользователей (например, неожиданно удачная реакция на рекламу), то у вас будет время скорректировать лимит. Можно рассчитывать лимит как 2 * среднее количество отправок смс за последние Х дней, чтобы не приходилось править его вручную по мере естественного роста посещаемости. Формулу и процент для алертинга, разумеется, подберёте под свои требования. Но можете взять и мои за основу.

    Отдельно хочу рассказать про так называемые прямые мобильные номера. Они выглядят как городские (например, +7495), но в реальности являются мобильными и могут принимать смски. Проверка в пункте 1 не пропускает такие номера и нет никакого способа проверить без отправки смс является ли отдельно взятый городской номер прямым мобильным. Прямых номеров мало по сравнению с настоящими мобильными или настоящими городскими. К тому же у каждого прямого номера есть мобильный аналог, начинающийся на +79, которым пользователь может воспользоваться для регистрации. Поэтому предлагаю просто забить на прямые номера, а в случае жалоб на невозможность регистрации с прямым номером, рассказывать про существование мобильного аналога, который может быть прописан где-то в договоре с оператором на оказание услуг связи или узнать в техподдержке оператора и с которым можно спокойно зарегистрироваться.

    Обязательное требование email'а не усилит схему защиты, так как не проблема наштамповать реальных адресов со скриптом, автоматически прокликивающим подтверждающие ссылки во входящих письмах.

    Альтернативным вариантом является аренда входящего номера. В этом случае не вы отправляете сообщения пользователям, а они вам. Обычно за входящий номер берут фиксированную плату в месяц независимо от количества смс, так что не придётся в принципе волноваться за бюджет. Но тогда у вас будут в пролёте пользователи с отключенной услугой отправки смс. А таких немало, могу сказать. Благодаря интернет-мессенджерам.
    Ответ написан
    2 комментария
  • Запуск крон. Доступ запрещен?

    Vamp
    @Vamp
    Вам нужно явным образом вызвать интерпретатор php и передавать ему путь к скрипту:
    cd /var/www/no-name/data/www/oushd.ru/core/channels/channels/ && php /var/www/no-name/data/www/oushd.ru/core/channels/channels/test.php
    
    cd /var/www/no-name/data/www/oushd.ru/core/channels/channels/ && php /var/www/no-name/data/www/oushd.ru/core/channels/channels/channels.php
    Ответ написан
    1 комментарий
  • Какой подход для валидатора правильней?

    Vamp
    @Vamp
    Архитектурно правильнее возвращать как в примере с Yii2. Использование исключений для управления потоком выполнения программы считается серьёзным антипаттерном.

    Например:
    1. Исключения в данном случае - просто более хитрая замена оператору GOTO. Надеюсь, не нужно объяснять почему GOTO тоже считается антипаттерном?
    2. Программы, написанные в таком стиле, становится сложно читать и понимать.
    3. Меньшая эффективность в рантайме, так как большинство современных компиляторов не оптимизированы для случаев использования исключений в качестве управляющей логики.

    Более подробно по ссылке.
    Ответ написан
    5 комментариев