• Как шифровать личные данные пользователей?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Давайте различать. Шифрование пароля - это не то же самое, что шифрование других данных. Пароль следует не шифровать, а хешировать. Это такое шифрование, которое нельзя расшифровать обратно. То есть имея хеш нельзя получить пароль, а имея пароль можно получить точно такой же хеш. Существуют для этого специальные хеш функции. Но хешировать пароли мало, их нужно сперва солить. Соль - это произвольный текст, присоединённый к паролю перед хешированием и размещаемый рядом с хешем в открытом виде. Нужна соль для того, чтобы нельзя было подбирать простые пароли по значению их хешей.
    Проверка пароля будет такой:
    1. Запрашиваем у пользователя логин и пароль.
    2. Достаём из БД по логину хеш солёного пароля.
    3. С этой солью хешируем введённый пользователем при авторизации пароль и сличаем хеши. Совпали -- значит пускаем.

    Шифрование других данных, очевидно, нужно уже обратимое, чтобы можно было расшифровать. И теперь есть два варианта: серверное и клиентское шифрование.

    Серверное не имеет смысла, поскольку скомпрометированный сервер означает утечку всего необходимого для расшифровки данных. Не скомпрометированный сервер означает, что данные и так вроде бы в безопасности. Значит шифровать не нужно (ну кроме некоторых специальных случаев).

    Клиентское шифрование - это когда сервер не имеет возможности расшифровать данные. Они шифруются на клиенте перед отправкой ключом, который не покидает пользовательского компьютера. Потом клиент снова запросит шифрованные данные с сервера и расшифрует его тоже сам. Это иногда имеет смысл. Например если вы храните keychain с паролями на сервере, но не хотите их утечки в случае взлома сервера.

    Есть ещё p2p шифрование, где с помощью специального алгоритма пользователи обмениваются ключами через сервер так, чтобы эти ключи не мог узнать ни сервер, ни кто иной. Далее от пользователя к пользователю ходит через сервер шифрованная информация, которую кроме оконечных пользователей никто не может расшифровать. Это так называемое оконечное шифрование.

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

    О, чуть не забыл. Есть опасность утечки незашифрованных данных из датацентров при наличии физического доступа к жестким дискам. Чтобы обезопасить себя в этом смысле, можно применить прозрачное шифрование файловой системы. Работающая операционная система будет знать ключ для расшифровки данных, но отсоединённый диск становится без ключа бесполезным. Однако это малоэффективно, если украдут весь сервер вместе с дисками. Зато эффективно против восстановления жуликами данных, если диск сгорел, а его сисадмин выбросил не просверлив.

    Ещё один аспект - это канал передачи. Нет смысла опасаться перехвата незашифрованных данных в канале передачи даже на последней миле провайдера клиента. Об этом заботится SSL когда правильно настроен HTTPS и сертификаты не скомпрометированы, а пользователь не подмахнул левый сертификат.

    Если вы задаёте вопрос о необходимости шифрования, значит вам ничего сверх вышесказанного шифровать не нужно. Это усложнит систему, сделает её менее надёжной, более (как ни парадоксально) уязвимой и отнимет ресурсы процессора, памяти, не даст использовать или усложнит кэширование и прочие оптимизации.
    Ответ написан
    Комментировать
  • Насколько подходит предложенный мной вариант защиты python кода?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Тоже попробую пояснить.
    При разработке защиты ПО всегда нужно сопоставлять цену взлома и профит от взлома.
    Арифметика примерно такая.
    Если цена взлома в k раз меньше суммарного профита от взлома (с учетом масштабирования) и сильно не превышает цену повторного написания приложения, то продукт будет взломан и это вопрос времени.
    Это пуассоновский процесс и чем больше k тем быстрее произойдёт вероятный взлом.

    Разработчик защищая продукт огребает существенный геморрой с поддержкой, отладкой, диагностикой, обновлением и расширением ПО.
    Цена этого геморроя может оказаться не сильно ниже ущерба от взлома. Тогда такую защиту применять не стоит. Особенно если ваш продукт развивается и растёт, в нём регулярно появляются новые фичи и каждый раз ломать дорого (даже с учетом, что повторный взлом дешевле первого).

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

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

    Хороший вариант - это глубокая обфускация. Исходники в оригинальном виде из обфусцированного кода получить не выйдет, их проще написать заново. А вот работающую программу получить можно.
    Очевидно, что делать это будут не рядовые пользователи, а профессионалы и за деньги. Для защиты от рядовых неквалифицированных пользователей достаточно поксорить байткод=).

    В итоге. У вас с вашим списком мер есть шанс построить бастион вокруг сортира. Если сортир с золотыми унитазами, то их всё равно сопрут, а если нет, то нафиг столько геморроя?

    Если у вас в коде есть алгоритмические участки, которые можно изолировать и трудено повторить или эмулировать (это должны быть нужные функциональные участки реально постоянно работающего кода), то их можно вынести в крипто-ключ в виде USB-донгла. Это тоже немалый гемор, но порой вполне применимый.

    Я не знаю что у вас за продукт, стало бы понятнее при наличии подробностей, но в общем случае хорошо работают и достаточно эффективны (если речь не идёт о "ломе") следующие меры в порядке убывания удобства:
    1. вынос функциональности на сервер в виде API
    2. байткод без исходников
    3. обфускация (лёгкая)
    4. обфускация (тяжелая, с поддержкой полиморфности кода)

    Всякие упаковки в экзешники и шифрование - это уже так... от лукавого.
    Просто вынесите ключевой ресурс на сервер и всё.
    Ответ написан
    Комментировать
  • Как поменять данные в JSON из Python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Да, JSON нецелесообразно менять прямо на диске. Его нужно загрузить, распарсить, заменить значение, потом снова сериализовать данные в JSON и сохранить на диск.
    import json
    with open('myfile.json') as f:
        data = json.load(f)
    data['example1'] = 'bye'
    with open('myfile.json', 'w') as f:
        json.dump(data, f, ensure_ascii=False, indent=4)
    Ответ написан
    8 комментариев
  • Как опубликовать модуль на pypi?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Ответ написан
    Комментировать
  • Как проверить регуляркой значение инпута которое будет идти после скобки?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Есть замечательный инструмент для отладки регекспов: https://regex101.com/
    Вот пример регулярки, которая решает вашу проблему:
    .*\((\d+)
    Первая группа даст вам ваше число.
    Подробнее: здесь жадно пропускаются все символы до скобки, за которой есть хотя бы одна цифра. Все цифры после этой скобки собираются в группу 1.
    Этот же сайтик позволяет сгенерировать код примера на нескольких языках:
    const regex = /.*\((\d+)/gm;
    const str = `8(354)1234
    8(354 456)789
    `;
    let m;
    
    while ((m = regex.exec(str)) !== null) {
        // This is necessary to avoid infinite loops with zero-width matches
        if (m.index === regex.lastIndex) {
            regex.lastIndex++;
        }
        
        // The result can be accessed through the `m`-variable.
        m.forEach((match, groupIndex) => {
            console.log(`Found match, group ${groupIndex}: ${match}`);
        });
    }
    Ответ написан
  • Как запустить андроид эмулятор?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Я использовал для этой цели virtualbox.
    Он имеет интерфейс командной строки и позволяет запускать машину через неё.
    С запуском же приложения в андроиде так просто вопрос не решить.
    Нужно ставить какую-то тулзу для удалённого управления андроидом, я думаю.
    Встречал такие, но не искал из с CLI. Наверно есть. Но если нет, то на андроиде вполне можно поднять sshd, и я не вижу причин почему бы не делать через него всё что угодно.
    Ответ написан
    2 комментария
  • Как перенаправить трафик только 1 сайта через VPN?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Мы решили вопрос доступа к API телеграма так.
    Стандартный домен API телеги для ботов заблочен роскопозором, поэтому в конфиге нашего бота подменён домен для доступа к АПИ.
    Наш же VDS за пределами юрисдикции роскомпозора в конфиге nginx имел такую вот запись:
    server {
        listen tg.my_own_domain.ru:8079;
        server_name tg.my_own_domain.ru;
        access_log /var/log/nginx/tg.my_own_domain.ru-access.log;
        error_log /var/log/nginx/tg.my_own_domain.ru-error.log;
        location / {
            proxy_set_header X-Forwarded-Host $host;
            proxy_set_header X-Forwarded-Server $host;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass https://api.telegram.org/;
            client_max_body_size 100M;
        }
    }
    Ответ написан
    Комментировать
  • Какой выбрать жанр для андроид игры?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Не знаю какие актуальны, но могу накидать более-менее оригинальных идей.
    Это только часть, есть ещё=).
    Игра 'Дворник'

    Дополненная реальность и геолокация позволит вам гуляя с телефоном грести перед собой виртуальный снег. Снег выпадает периодически (можно синкануть с реальной погодой), у игрока есть лопата определенной ширины. Вдоль трека в реальном времени собирается снег в кучу. Когда лопата переходит в насыщение, формируются отвалы слева и справа от трека. Снег можно сгребать в кучи или разгребать. "растаскивать" чужие кучи. Кто залезет на самую большую кучу, раз в час может получить новую лопату.

    Идея в том, что снег общий на общей реальной карте. Играть ногами.
    Могу помочь с серверной частью как кодом, так и советом.

    Arty Fight

    Геолокационная массовая межконтинентальная артиллерийская онлайн-игра с дополненной реальностью и элементами tower defence.

    На реальной глобальной карте строим мегалитические убер-пушко-катапульты. Запускаем с их помощью в направлении соседнего города, страны, континента
    всякое: от шаров с краской и гигантских наковален до коров и тортиков.

    С помощью мобильного телефона можно увидеть пролетающее над головой, а на карте остаются пятна и останки "снарядов". Можно забомбить рыбой Иерусалим или милыми хрюшками Багдад. Можно подцепиться к публичным городским камерам наблюдения и дорисовывать падающее на город в режиме дополненной реальности.

    Да, время от времени можно сбрасывать на территорию "гуманитарную помощь" с полезными и не очень сюрпризами. Как в старом добром Worms Armageddon.
    Можно развернуть над столицей или своей кафешкой гигантский силовой щит, можно отстреливать на лету медленные снаряды, ставить "батуты" и отправлять "наковальни" обратно.

    Игра на общей карте с захватом территорий кругами

    • концепция чем-то похожа на игру "Ножички"
    • игрок оказывается в произвольном месте наобщей карте и ему принадлежит круг определенного радиуса.
    • в IDLE режиме юнит ходит только по периметру своей территории, пользователь указывает ему точку дуги. которую следует укреплять.
    • в IDLE накапливается какой-то ресурс для "Рывка", например появляется новый юнит и старого можно послать в "Рывок"
    • с некоторой частотой он может сделать "Рывок" - побежать по кругу наружу от своей границы и добежав до своего же периметра откусывает новый кусок территории. С ростом территории растёт и протяженность границ
    • радиус забега можно фиксировать, его можно повышать и бустерами
    • бустеры и прочие сюрпризы падают с неба, как, впрочем и метеориты с бомбами, которые отхватывают от твоей территории такие же круглые куски
    • скорость/частота событий может зависеть от радиуса/площади
    • ПВП:
      • В Рывке можно захавать кусок территории противника, если удастся прорваться за участок его забора за доступное время (секунды).
      • Если противник оказался онлайн, можно попробовать пересечь след выбежавшего в Рывок юнита и юнит будет потерян/захвачен, если окажется окружен. внутри территории врага.
      • можно подумать над агломерациями союзников.

    IDLE игра на огромном глобальном гексагональном поле

    • С некоторой частотой можно занимать ячейки рядом со своими
      • перемещением одной из своих занятых ячеек (быстрее/чаще)
      • размножением одной из своих занятых ячеек (медленнее/реже)

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

    Игра на гекссагональном поле

    - для 2, 3, 4, 6 игроков, размещенных симметрично
    - у каждого есть стартовая ячейка..
    - каждый ход занимает произвольную соседню из свободных
    - из лекции Саватеева "Теория игр"

    RIP Tumi (рвать газетку)

    Мобильная игра: рвать газету на тонкие полосы
    • название игры состоит из фамилии Крейга Туми, одного из героев фильма "Лангольеры" Стивена Кинга, и его основного занятия -- рвать бумагу
    • геймплей:
      • на экране скроллируемая область с большой газетой или страницей журнала
      • если начать медленный свайп от края листа, то он будет рваться в сторону свайпа
      • если дёрнуть слишком быстро, то разрыв произойдёт до ближайшего края

    • метрики:
      • время:
        • разрывания
        • игры
        • скорость (максимальная, средняя, по газете...)


    • рейтинги:
      • среднее арифметическое длин всех оторванных кусков текущей газеты (вычисляется в реальном времени)
      • суммарная длина всех оторванных кусков текущей газеты (вчисляется в реальном времени)
      • количество кусков текущей газеты
      • среднее арифметическое длин всех оторванных кусков в пределах профиля
      • суммараня длина всех оторванных кусков в пределах профиля
      • суммарное количество всех клочков по профилю


    • ачивки:
      • достижения длин, сравнимых с длинами:
        • самолёта
        • ВПП
        • расстояний между городами
        • экваторов различных планет
        • расстояний до планет и небесных тел


    • достижение количеств, сравнимых с интересными количествами
    • достижение объёмов порванной бумаги, сравнимых с интересными объёмами (вычисляется по эмпирическому коэффициенту)

    • монетизация:
      • реклама на разрываемых газетах



    Черепашьи бега

    Айдлер. На единой карте (в пределах сессии или всего мира) бегают по маршруту черепашки. Цель - пройти маршрут по пилонам.
    Черепах можно приманивать и отпугивать (едой, кактусами и т.д.). Всего этого ограниченное количество, выпадает и пополняется периодически.
    Ответ написан
    Комментировать
  • Как работает перевод в другую систему исчисления Python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Если на входе строка, то перевод в целое значение (в двоичный вид) осуществляется стандартной функцией int, у которой есть необязательный аргумент, указывающий на систему счисления.

    Конвертация числа в n-ричную систему производится рекурсивно.
    Если число на входе меньше базы, то возвращаем соответствующий по счету символ алфавита.
    Если число больше или равно базе, то возвращаем символ алфавита, соответствующий остатку от деления на базу, а оставшееся после деления число снова отдаётся этой же функции рекурсивно.

    В n-ричной системе счисления каждая очередная цифра числа - это очередной остаток от деления числа на базу.
    Ответ написан
    Комментировать
  • Как правильно получать кадр фильма без артефактов?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Попробуйте вытаскивать из видео не кадр, а фрагмент. Эмпирически можно подобрать его длину, чтобы гарантированно попался ключевой кадр.
    Из вырезанного фрагмента, сохранённого уже без сжатия можно отдельным вызовом ffmpeg получить последний кадр.
    Ответ написан
  • Как с помощью API работать с Яндекс картами?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Для 37.677751,55.757718:

    https://static-maps.yandex.ru/1.x/?ll=37.677751,55.757718&size=200,200&spn=0.016457,0.00619&l=map

    https://static-maps.yandex.ru/1.x/?ll=37.677751,55...
    Ответ написан
    Комментировать
  • Как очистить таблицу кроме последних N записей?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Я бы одним SQL-запросом это сделал.
    DELETE FROM my_history_table t 
    WHERE t.id <= (
        SELECT t2.id
        FROM my_history_table t2 
        ORDER BY datetime_open DESC
        SKIP 20
        LIMIT 1
    )

    Диалект SQL подправить по вкусу.
    Ответ написан
    Комментировать
  • Как правильно распарсить логи с помощью bash?

    trapwalker
    @trapwalker
    Программист, энтузиаст
    Вот такая штука будет работать примерно со скоростью мегабайт в секунду.
    cat oldlogfile.log | py -x "', '.join(['='.join((k, datetime.datetime.strptime(v, '%Y-%m-%d %H:%M:%S').strftime('%H:%M:%S.000 +0700 %a %b %d %Y')) if k == 'TIME' else (k, v)) for k, v in ((kv.split('=') for kv in x.split(', ')))])" > newlogfile.log

    Измерял так:
    yes "RESULT=xxxxx, TIME=2020-01-20 18:43:12, HOST=xxxxxxxxxxx, NAME=xxxxxxxx" \
      | pv \
      | py -x "', '.join(['='.join((k, datetime.datetime.strptime(v, '%Y-%m-%d %H:%M:%S').strftime('%H:%M:%S.000 +0700 %a %b %d %Y')) if k == 'TIME' else (k, v)) for k, v in ((kv.split('=') for kv in x.split(', ')))])" \
      > /dev/null

    Зато на порядок понятнее и можно по человечески формат подправить.
    Ответ написан
    3 комментария
  • Как записывать в фаил чтобы можно было редактировать вручную?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    json.dumps(my_data, ensure_ascii=False)
    Там выше правильно ответили ссылкой на документацию.
    Аргумент ensure_ascii требует экранировать не ASCII символы при сериализации. Если его поставить в False, то будет то, что вам нужно.
    Ответ написан
    Комментировать
  • Как не прерывать python при ошибках, которые не критичны для приложения?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Есть ещё удобный контекстный менеджер для подавления исключений в случаях, когда не требуется какой бы то ни было обработки.
    from requests.exceptions import ConnectTimeout
    from contextlib import suppress
    
    with suppress(ConnectTimeout):
        # code
    Ответ написан
    Комментировать
  • Как исправить ошибку 'ascii' codec can't decode byte 0xd0 in position 0: ordinal not in range(128)?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    yourorder = yourorder.decode('utf-8')  # или 'cp1251' или 'cp866' или что та у вас
    district = district.decode('utf-8')
    bot.edit_message_text(
        chat_id=c.message.chat.id,
        message_id=c.message.message_id,
        text= u'{}Страна: {}\n\nВыбери товар'.format(yourorder, district), 
        reply_markup=key,
    )

    Ещё вы смешиваете конкатенацией юникод-строки и неюникод ('\n\n'). Не надо так.
    Ответ написан
    Комментировать
  • Как получить русский текст из json на Python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Не надо там ничего энкодить и декодить.
    В ответе текст в юникоде и парсится функцией `json.loads` адекватно.
    Проблема скорее всего у вас из-за кодировки в консоли винды. там какая-нибудь однобайтовая кодировка вроде cp1251 или cp866.
    При попытке напечатать юникод в этом терминале вы получаете ошибку из-за того, что при автоматическом преобразовании из юникода в кодировку консоли питон пытается взять кодек по умолчанию, который, конечно 'ascii'.

    Винда такая винда со своим беспощадным терминалом и кодировками по умолчанию.

    Но вы можете напечатать этот текст, в нём нет непечатных символов для однобайтовой кодировки. Попробуйте так:

    x = obj['result'][-1]['status']
    try:
        print('cp1251:', x.encode('cp1251'))
    except:
        try:
            print('cp866:', x.encode('cp866'))
        except:
            print('no way')


    Общие правила работы с кодировками такие:
    - на входе в программу мы всё преобразовываем в юникод.
    - на выходе всё кодируем в нужную кодировку.
    - если выход - это печать в стандартный вывод (stdout), то ситуаций может быть 4:
    1) печатаем в терминал винды и терминал у нас в кодировке 1251
    2) печатаем в терминал и он у нас в 866 кодировке
    3) печатаем в stdout, который перенаправлен в файл и пайп не знает в какой он кодировке, то есть кодировка не задана и мы можем заэнкодить в любую и в файл это запишется. Пользутейс utf8 - самая правильная кодировка для всего.
    4) вы в линуксе и терминал у вас в кодировке по умолчанию - utf8 и всё хорошо.

    В любом случае, печатая или сохраняя что-то в файл вы должны понимать, что текст нужно закодировать в кодировку. Это может произойти неявно (как в ашем случае) но при попытке закодировать в кодировку по умолчанию (ascii) не каждый символ в ней можно представить. В ASCII всего 127 символов. Получилась закономерная ошибка.

    У потоков стандартного ввода/вывода есть атрибут encoding:
    import sys
    sys.stdout.encoding  # 'UTF-8'

    В вашем случае будет либо None, если вывод перенаправлен в файл,
    либо 'cp1251', либо 'cp866' ну или ещё что-нибудь эдакое.
    Если не None, то в эту кодировку можно постараться заэнкодить вашу строку. По-прежнему некоторые символы могут не конвертнуться (не в вашем случае), их можно игнорировать специальным аргументом метода encode.
    Ответ написан
    1 комментарий
  • Как корректно извлечь данные из CSV-датасета?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    import csv
    with open('dataset.csv') as f:
        reader = csv.reader(f)
        for row in reader:
            print(row)

    Можно воспользоваться csv.DictReader, если полей очень много и с ними удобнее работать как со словарями по имени.

    Могут быть нюансы:
    - проблемы с кодировкой файла (решается указанием кодировки при открытии);
    - необычные разделители (решается указанием или настройкой диалекта);
    - данные изображений, скорее всего, не лежат прямо в csv (хотя могли бы=), там скорее всего имена файлов. Не думаю что корректно загружать весь датасет для обучения целиком. Лучше это делать в потоке по одному элементу и забывать использованные сразу не оставляя в памяти.

    Если уже используете pandas, то решение выше вам подойдёт лучше.
    Ответ написан
    Комментировать
  • Почему не отображаются данные?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    Вы не оттуда импортируете `QTableWidgetItem`. Уберите его из импорта и там, где он опоминается напишите `QtGui.QTableWidgetItem`.
    Внутри осуществляется проверка на конкретный класс, а этот класс у вас существует дважды в разных модулях. Об этом чётко сказано в сообщении об ошибке.
    Ответ написан
  • Какой алгоритм выборки данных из списка Python?

    trapwalker
    @trapwalker Куратор тега Python
    Программист, энтузиаст
    s_list = [
        {'one': 1, 'two': 2, 'seven': 7, 'fix': 'price', 'number': [5, 4, 2, 3, 5, 4], 'dig': 4},
        {'one': 5, 'two': 4, 'seven': 6, 'fix': 'nix', 'number': [3, 5, 7, 2, 3, 9], 'dig': 5},
        {'one': 8, 'two': 3, 'seven': 9, 'fix': 'pix', 'number': [3, 2, 3, 1, 8, 4], 'dig': 9}
    ]
    for i, item in enumerate(s_list):
        print('\nItem #', i, ':')    
        for key in item:
            print(key, '=', item[key])
    Ответ написан
    1 комментарий