Django python — загрузка csv и сохранение в базу данных

Это руководство для новичков в Django, которые прочитали начальное руководство по Django на официальном сайте и столкнулись с задачей загрузки csv файла с таблицей в базу данных

В этом случае, у вас есть приложение polls, с урлами, моделями, вьюшками для классов вопросов (Question) и ответов (Choices). И база данных с сгенерированными таблицами (polls_question, polls_choice)

Для примера, будем загружать в базу данных csv файл с вопросами (Questions). 

Назовем файл questions_01a.csv и поместим туда такое содержимое:

Why?,2022-11-14 10:00
For what?,2022-11-14 10:01
When?,2022-11-14 10:02
Where?,2022-11-14 10:03

Перенос строк — как разделитель информации по вопросам, и запятая — как разделитель ячеек данных

Изменения в /polls/urls.py:

from django.urls import path

from . import views

app_name = 'polls'
urlpatterns = [
    …
    path('upload_csv/', views.upload_csv, name='upload_csv'),
]

Изменения в /polls/views.py:

from django.http import HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.views import generic
from django.utils import timezone
from django.contrib import messages

from .models import Question, Choice

…

def upload_csv(request):
    data = {}
    if "GET" == request.method:
        return render(request, "polls/upload.html", data)
    # if not GET, then proceed
    try:
        csv_file = request.FILES["csv_file"]
        if not csv_file.name.endswith('.csv'):
            messages.error(request, 'File is not a CSV')
            return HttpResponseRedirect(reverse("polls:upload_csv"))
        # if file is too large - error
        if csv_file.multiple_chunks():
            messages.error(request, "Uploaded file is too big (%.2f MB). " % (csv_file.size/(1000*1000),))
            return HttpResponseRedirect(reverse("polls:upload_csv"))

        file_data = csv_file.read().decode("utf-8")

        lines = file_data.split("\n")
        # loop over the lines and save them to db via model
        for line in lines:
            fields = line.split(",")

            try:
                question = Question(
                    question_text=fields[0],
                    pub_date=fields[1],
                )
                question.save()

            except Exception as e:
                messages.error(request, "Unable to upload file. "+repr(e))
                pass

    except Exception as e:
        messages.error(request, "Unable to upload file. "+repr(e))

    return HttpResponseRedirect(reverse("polls:upload_csv"))

Что делаем в views.py:

  • Импортируем стандартный фреймворк сообщений (messages), чтобы можно было на страничке выводить ошибки
  • Файл будем посылать методом POST, если же запрос GET — открываем страницу аплода
  • Делаем простые проверки получаемого файла (тип файла и максимальный размер)
  • Читаем построчно и сохраняем в базу данных, через модель

Создаем страничку для загрузки файла — /polls/templates/polls/upload.html со следующим кодом:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Upload</title>
    <link href="https://netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <form action="{% url "polls:upload_csv" %}" method="POST" enctype="multipart/form-data" class="form-horizontal">
        {% csrf_token %}
        <div class="form-group">
            <label for="name" class="col-md-3 col-sm-3 col-xs-12 control-label">File: </label>
            <div class="col-md-8">
                <input type="file" name="csv_file" id="csv_file" required="True" class="form-control">
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-3 col-sm-3 col-xs-12 col-md-offset-3" style="margin-bottom:10px;">
                 <button class="btn btn-primary"> <span class="glyphicon glyphicon-upload" style="margin-right:5px;"></span>Upload </button>
            </div>
        </div>
        {% if messages %}
        <ul class="messages">
            {% for message in messages %}
            <li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
            {% endfor %}
        </ul>
        {% endif %}
    </form>
</body>
</html>

Что делаем в upload.html:

  • Минималистичная html страница
  • Подключаем стили бутстрап для более-менее симпатичного отображения элементов
  • Форма с кнопкой Загрузки
  • CSFR токен для защиты формы от размещения ее на внешних источниках, чтобы украсть ваши данные (стандартная защита)
  • Блок для отображения ошибок messages

Запускаем проект и пробуем открывать страничку 

http://127.0.0.1:8000/polls/upload_csv/

Загружаем наш заранее заготовленный файл questions_01a.csv и проверяем базу данных на наличие новых строк

Выглядит это так:

Оптимизация запросов MySQL — ускоряем запросы

  • Одна из распространённых причин медленных запросов — обработка лишних данных

Это может быть запрос лишних строк из большой таблицы, где не все они нужны. Можно использовать LIMIT или ограничивать выборку WHERE

SELECT * FROM huge_table LIMIT 10;
-- или
SELECT * FROM huge_table WHERE id BETWEEN 0 AND 1000;

Это может быть запрос всех столбцов в таблице с большим их количеством или в нескольких таблицах, соединенных JOIN. Лучше, запрашивать только необходимые столбцы

SELECT id, name, email FROM users LIMIT 1000;

Это может быть повторный запрос за теми же данными. В этом случае, если не получается избежать, стоит кешировать результаты и использовать данные из кеша

  • Другая причина медленных запрсов — анализ лишних данных

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

Каким образом и сколько данных анализируется, можно посмотреть используя команду EXPLAIN и строка rows в результатах

  • Эффективным способом ускорения сложного запроса может быть разбиение его на более простые, которые выполняются быстрее первоначального
-- Было
SELECT * FROM tag 
JOIN tag_post ON tag_post.tag_id = tag.id
JOIN post ON tag_post.post_id = post.id
WHERE tag.tag_name = ‘mysql’;

-- Стало
SELECT * FROM tag WHERE tag_name = ‘mysql’;
SELECT * FROM tag_post WHERE tag_id = 1234;
SELECT * FROM post WHERE id IN (123, 456,789);
  • Или разбиение очень длительного запроса на более короткие порции
-- Было
DELETE FROM messages WHER created < DATE_SUB(NOW(), INTERVAL 3 MONTH);

-- Стало
DELETE FROM messages WHER created < DATE_SUB(NOW(), INTERVAL 3 MONTH) LIMIT 1000; 
-- несколько раз
  • Оптимизатор MySQL плохо оптимизирует корректированные подзапрос вида
SELECT * FROM films WHERE id IN (
    SELECT film_id FROM film_actors WHERE actor_id = 100
);

В этом случае, лучше разделить и связать их на бекенде

SELECT film_id FROM film_actors WHERE actor_id = 100;
-- И затем
SELECT * FROM films WHERE id IN (1,5,9,13,77);
  • Не стоит злоупотреблять сортировкой ORDER BY, так как, без индекса на сортируемом столбце, она может значительно увеличить время выполнения запроса. Можно использовать LIMIT для ускорения сортировки, если она необходима
  • Запросы с UNION часто идет с ограничением LIMIT, и для ускорения, следует добавить LIMIT в оба под-запроса
-- Было
(SELECT id, name FROM film_actors order by name)
UNION ALL
(SELECT id, name FROM theatre_actors order by name)
LIMIT 20;

-- Стало
(SELECT id, name FROM film_actors order by name LIMIT 20)
UNION ALL
(SELECT id, name FROM theate_actors order by name LIMIT 20)
LIMIT 20;
  • Запросы с LIMIT и OFFSET на больших таблицах плохо работают с большими OFFSET, а это часто встречается в пагинации

Например для:

SELECT * FROM users ORDER BY id LIMIT 100 OFFSET 40000;

Сервер сгенерирует  40100 строк и отбросит первые 40000

Стоит заменить на:

SELECT * FROM users WHERE id > 40000 ORDER BY id LIMIT 100;
  • Ну и на последок, на бекенде часто используется ORM для генерации запросов. И нужно понимать, что для сложных запросов ORM формирует неоптимальный медленный запрос. В таком случае, следует переписать ORM запрос на чистый SQL

Оптимизация запросов MySQL — архитектура таблиц

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

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

Например, IP адреса лучше хранить в целочисленных данных, так как сравнение строковых данных работает намного медленнее. 

Даты и время рекомендуется хранить во встроенных типах данных MySQL. Из DATETIME и TIMESTAMP, последний предпочтительнее, так как требует вдвое меньше места для хранения

  • Целые числа: TINYINT (8 бит), SMALLINT (16 бит), MEDIUMINT (24 бит), INT (32 бит), BIGINT(64 бит) и диапазоны значений от -2^(N — 1) до 2^(N — 1). В зависимости от планируемого диапазона значений — выбираем более простой тип

Часто, отрицательные значений не требуются (например, авто-инкрементирующийся ID) и можно расширить диапазон в два раза благодаря атрибуту UNSIGNED.

  • Вещественные числа занимают значительно больше места.

Расчеты с данными с плавающей точкой высокозатратные и могут приводить к ошибкам на бекенде, когда «1» на самом деле берется как «0.9999987». В том числе поэтому, целесообразнее выбрать Целочисленный тип вместо Вещественного. Если нам известно, что у данных максимум 2 знака после запятой, «109.40» храним и обрабатываем как «10940»

  • Наиболее часто используемый строковый тип — VARCHAR. Используется при хранении данных переменной длины.
  • В отличии от VARCHAR, CHAR имеет фиксированную длину и лучше оптимизирован для сравнения строк с примерно одинаковой длинной (например для хранения MD5 сверток данных пользователя). 

CHAR так же эффективен для хранения часто изменяемых данных, так как не подвержен фрагментации при записи

  • Для хранения больших данных используются: TEXT — символьные данные, BLOB — двоичные данные
  • Если количество вариаций данных ограничено, то можно использовать ENUM

ENUM работает быстро, так как, по сути, хранит целое число. Недостатком ENUM будет то, что при добавление нового значения в набор, нужно будет менять схему таблицы (ALTER TABLE) или писать миграцию на бекенде

  • Лучше избегать значений NULL, если они не нужны. 

Стоит объявлять столбец как NOT NULL. MySQL тяжелее оптимизировать запросы к столбцам, допускающим NULL

Оптимизация запросов MySQL — производительные индексы

Индексы (или ключи) — это специальные структуры данных, которые использует подсистемы хранения для ускорения нахождения строк.

  • Индексы бесполезны, и, даже, вредны, если данных в таблицы мало (тысячи или меньше строк)
  • Если данных много (десятки тысяч строк и больше) и есть запрос WHERE по одному столбцу (или же сортировка ORDER BY по этому столбцу), который выполняется часто, лучше «повесить» на него простой индекс
  • Индекс хорошо работают по полному значению, диапазону значений
  • Составной индекс включает набор индексов (например, по трем столбцам A + B + C)
  • В это случае, индекс хорошо работает по полному набору проиндексированных столбцов («A + B + C»), по префиксам (левой части индекса: «A» или «A + B») и НЕ работает по пост-фиксам (правой части индекса: «C» или «B + C»). В случае необходимости поиска по «B» или «B + C», можно создать отдельный индекс — «B + C». Опять таки, индекс «B + C» будет неэффективен при поиске по C.
  • Альтернативой при существующем индексе «A + B + C» и необходимости поиска по пост-фиксу индекса, может быть включение в поиск столбца A искусственным образом:
SELECT * FROM TABLE WHERE B=2 AND C=3;
-- преобразуем в:
SELECT * FROM TABLE WHERE A="2022-10-14" AND B=2 AND C=3;
  • Так же, необходимо рассмотреть возможность смены порядка следования столбцов в запросе WHERE. Если, имеем индекс «A + B + C», при этом наш запрос выглядит так:
-- было:
SELECT * FROM TABLE WHERE A = 1 AND C = 2 AND B = 3;
-- стало:
SELECT * FROM TABLE WHERE A = 1 AND B = 3 AND C = 2;
  • Нужно понимать, что, если в запросе есть поиск по одному столбцу и сортировка по другому, хорошо подойдет составно индекс по этим столбцам и плохо будет работать отдельный индекс по одному из столбцов
  • В случае индексов на столбец строкового типа, индекс хорошо работает по полному совпадению и при поиске по префиксу (WHERE column LIKE «abc%»). При это у индекса есть ограничения, и по длинной строке поиск будет работать плохо
  • Чем меньше памяти потребляет тип столбца, тем быстрее работает и индекс. Поэтому поиск по индексированному целочисленному столбцу гораздо быстрее, чем по строковому
  • В случае длинных строк, и необходимости поиска по ним, можно ввести индексируемый столбец с хешем по длинной строке.

Хороший пример данных, это столбец с адресом веб-страниц url и поддержка столбца  c хешем этой строки — url_hash. При сохранение данных, записываем в столбец url оригинальный адрес веб-страницы, а в url_hash — хеш строки этого адреса (например кодировкой CRC32 или другой кодировкой в цифровое значение). А при поиске ведем поиск только по индексированному url_hash.

-- долгий запрос:
SELECT * FROM TABLE WHERE url = "https://long-adress-url.com";
-- заменяем на:
SELECT * FROM TABLE WHERE url_hash = CRC32("https://long-adress-url.com");
  • Другой частый случай плохо-индексируемых данных — это IP-адреса. Для них, так же есть специальный функции на бекенде, которые превращают строку IP-адреса в числовой тип для отдельного индексируемого столбца.

Конкретные функции зависят от языка программирования. Например, для PHP подойдет функция ip2long для шифровки и long2ip для расшифровки

Чистый код — избавляемся от грязного кода

Чистый код — меньше багов
  • Среда разработки помогает быстрее писать код. А еще, она подсказывает как сделать код чище. Стоит обращать внимание на все предупреждения среды разработки и учитывать их. Так же выявляется и дублирование кода
  • Дублирование — большая помеха чистоте кода. Простейшая форма дублирование — целые куски дублирующего кода. Их стоит выделять в отдельные функции, иногда — классы и пере-использовать. Более сложные примеры дублирования проявляются в одинаковых условиях if-else. Вместо них следует использовать принцип полиморфизма. Существуют еще более сложные случаи дублирования. Схожие алгоритмы могут соседствовать со специфичной логикой. Используем паттерны проектирования, например Шаблонный метод или Стратегия.
  • Дублирующий код может быть инкапсулирован в более высокие уровни абстракций, если необходимо пере-использовать его в других классах.
  • Не используемый код следует удалять, а не сохранять на будущее. Тоже происходит с “мертвым кодом”, до которого алгоритм никогда не дойдет. Это нередкая “болезнь” циклов if-else и switch-case
  • Вертикальное разделение помогает чтению кода. Переносы строк, без пропуска строк, покажут что код относится к одной логике. Пропуски строк разделят алгоритм на 2е логически-обособленные части
  • Такой балласт, как пустой конструктор или пустое тело цикла будет мешать читать код. Следует удалить их.
  • Пояснительные переменные помогают чтению и пониманию кода
$today = date('Y-m-d', now());
$targetDateString = '2020-07-08';
$targetDate = date('Y-m-d', strtotime($targetDateString));
If ($today >= $targetDate) {
	doTheJob();
}
  • Чистоте кода, так же, поможет соблюдение стандартных конвенций. Не обязательно это должен быть согласованный документ. Сам код служит примером оформления.
  • Так называемые “волшебные числа” заменяют именованными константами — пример кода со стр 339
$dailyPay = $hourlyPay * 8;
$PI = 3.14;
$circleLength = $radius * $PI * 2;

Конфигурационные данные выносим на высокие уровни абстракций — конфигурационные файлы или файл

Чистый код — комментарии

Чистый код — искусство комментирования

Комментарии

Комментарии к коду — не однозначная концепция. С одной стороны, комментарии используют для пояснения работы следующего кода. С другой стороны, одна из концепций “Чистого кода”: код должен быть понятным и без комментариев.

От сюда, обозначим основные концепции:

  • Комментарии быстро устаревают. Важно не забывать обновлять комментарии при обновлении следующего кода. Это известная проблема комментариев. Устаревшие комментарии могут быть вредны. Они описывают одно поведение, а алгоритм кода — другое, что путает программиста.
  • Комментарии увеличивают размер кодовой базы и время на изучение кода. Нужно прочитать не только код, но и комментарии, чтобы вникнуть в алгоритм. 
  • Есть такое заблуждение, что комментарии компенсируют плохой код. Например, пишем не очень понятный или запутанный код, а далее, в комментариях поясняем его работу. Лучше написать ясный и выразительный код, чтобы комментарии не потребовались
  • Замена комментариям — подходящие, говорящие имена переменных, методов и классов
  • Лучше удалять старый код, чем комментировать его. Закомментированный код со временем разрастается и создает большой объем бесполезного или вредного груза. 
  • В некоторых случаях комментарии уместны. Они будут мощным инструментом. 

Уместные типы комментариев:

  • Юридические комментарии в начале модулей или классов, копирайты
  • Пояснения к сложным регулярным выражениям или маскам
  • Если используются какие то стандартные имена из сторонних библиотек. При этом, мы не можем на них повлиять, но можем описать комментариями
  • Когда передают опыт использования или предупреждения (например, // код ниже может работать очень долго если количество записей в базе данных больше миллиона)
  • Комментарии TODO
  • Усиление значения какого то факта, если его значение может показаться неочевидным (например, //не удаляйте код ниже, так как это защита от…)
  • Комментарии вида APIdoc, необходимые для генерации авто-документации по методам АПИ

Чистый код — функции и классы

Чистые функции и классы

Классы и функции — важнейшие блоки построение кода проекта. Это делает их основными целями при работе с чистотой кода.

Рекомендации к написанию функций:

  • Функции должны быть как можно компактнее. Можно ориентироваться на размер максимум 20 строк, чтобы понимать масштабы
  • Функции могут содержать вложенности (циклы foreach, if else, while). Следует уменьшить число вложенностей до 1-2. В идеале одна вложенность. Больший уровень вложенности можно помещать в отдельные функции
  • Функция должна выполнять одну операцию. Это упрощает разделения алгоритма на функции
  • Если в функции есть “секции” (например объявление, инициализация, сбор данных и т.д.) значит она выполняет несколько операций. Следует разделить на разные функции
  • При этом имя функции следует выбирать таким образом, чтобы доходчиво объяснить операцию
  • Связанные функции следует размещать ближе к “родительским”. Создаем, таким образом, понятный уровень иерархии, который читается сверху вниз
  • Следует минимизировать количество аргументов функций. Большое количество аргументов усложняет понимание функции. Лучше всего читаются функции без аргументов. Они могут манипулировать полями классов ($this->variable). 
  • Если в функцию нужно передавать много аргументов, следует рассмотреть объединение их в класс, тогда из аргументов будет только один объект класса

Классы:

  • Оптимальный вариант строения класса: сначала идут открытые переменные, затем приватные. За списком переменных идут публичные функции. За каждой публичной функцией следуют приватные вспомогательные функции. При таком размещении, класс читается как газетная статья
  • Классы должны быть компактными. Размер класса может определить принцип единой ответственности. Этот принцип говорит, что класс должен иметь только одну причину для изменения. Пример класса с 2мя причинами для изменения ниже
Class UserDashboard {
    public function getLastFocusedElement() {…};
    public function setLastFocusedElement() {…};
    public function getMajorVersion() {…};
    public function getMinorVersion() {…};
}

Его стоит разделить на 2 класса: первый  — для работы с выделенным элементом дашборда, второй — для версионирования дашборда

  • Следует стремиться к бОльшей связанности в классах. БОльшая связанность достигается, когда бОльшая часть методов класса использует бОльшую часть переменных экземпляра класса. Разделение большого метода на серию более мелких снижает связанность в классе. После этого становится понятнее как разбить исходный класс на более компактные. В результате, повышается связанность и чистота классов, полученных из исходного класса.

Чистый код — простые концепции

Чистый код хорошо пахнет

Что такое “Чистый код”

Нету простого определения понятию “Чистый код”. Но можно тезисно описать некоторые подходы к пониманию:

  • Не содержит дубликатов
  • Четкий, простой и прямолинейный
  • Минимально-необходимое количество сущностей: классов, зависимостей, методов, функций, строк кода и т.д.
  • Понятный и выразительный
  • Продуманный. Создает впечатление, что автор кода над ним усердно потрудился
  • Реализованы все требования к продукту

Почти все это абстрактно, поэтому ниже будет конкретика

Почему важно писать чистый код

  • “Грязный код” накапливается. Это приводит к ошибкам. Количество ошибок растет лавинообразно. В конечном счете проект становится очень дорого поддерживать
  • Вы сами будете не раз возвращаться к своему предыдущему коду. Нужно будет вносить правки или улучшения. Чистый код проще поддерживать. Скорость разработки с чистым кодом выше
  • Не только вы будете изучать предыдущий код, но и ваши коллеги. Они почувствуют уважение и благодарность, если столкнуться с чистым кодом
  • Написание чистого кода так же будет развивать в вас такие положительные черты, как аккуратность, внимательность и предусмотрительность

Начало — простые концепции

Несмотря на кажущуюся простоту, они очень важные:

  1. Правило бойскаута

В контексте кода, правило бойскаута может звучать так: оставляем код чище, чем был до нашего прихода. Чистка не обязательно должна быть глобальной. Можно дать более удачное имя переменной, можно убрать замеченный дубль кода. Даже простые изменения в перспективе будут давать положительные результаты. 

Этот принцип формируем в нас характер и стремление к чистоте

  1. Содержательные имена

Имена должны передавать намерения программиста. При удачном именовании переменных, методов и класса, комментарии к коду не будут нужны. Сам код будет легко читаться и пониматься

Что следует избегать:

  • Короткие названия, которые слабо описывают смысл
grnp109, a1, pf
  • Однобуквенных переменных, где можно заложить смысл
$items = $this->userOrdersFromLastVisit($date);
foreach($items as $i) {
   $i->date = Date.now();
}

Может быть

$userOrders = $this->userOrdersFromLastVisit($dateOfLastVisit);
foreach($userOrders as $userOrder) {
    $userOrder->updatedDate = Date.now();
}
  • Сокращения и неудобно-читаемые имена

Было

mngmnt, len, datefromlastupdate

стало

management, length, dateFromLastUpdate

Другие рекомендации

  • Имена классов — обычно имена существительные: Product, UserPage, OrderParser
  • Имена методов — глаголы или глагольные словосочетания: deleteProduct, save, getName
  • Писать имена на английском языке без ошибок. Перевод любого слова можно получить в онлайн-переводчике
  • Быть последовательным. Если для получения данных в классе несколько раз использовался термин get, в похожем контексте слово fetch будет выбиваться

Защита аккаунта от взлома на веб-сайтах и соцсетях

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

Итак, тезисно:

  • Пароли. Существует мнение, что пароли должны быть сложными для того чтобы их не взломали. На самом деле, даже супер-сложный пароль могут подобрать. Особенно, если вы используете один и тот же пароль на разных сайтах. Некоторые сайты (со слабой защитой) могут взломать и базы данных логинов и паролей «утекут» в открытый доступ. Далее, злоумышленники обязательно попробуют именно этот логин и пароль на других популярных сервисах.

Конечно, пароль должен быть не из тех, что простые и распространенные. Но главное, все таки, это количество символов — не менее 10. И самое главное — не использовать один и тот же пароль для различных сервисов. Но чтобы не усложнять вам жизнь, существует такой лайфхак — добавлять к вашему «сложному паролю» несколько символов (систему) в зависимости от сервиса.

Предположим, ваш обычный пароль есть «ParOLb83561». Для YouTube вы можете использовать такой пароль «ParOLb83561-YT» (черточка как разделитель и две буквы, которыми можно зашифровать сервис), для TWitter такой «ParOLb83561-TW». И тогда сложный пароль становится уникальным

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

Наиболее распространённые разновидности: с помощью СМС и подтверждающих кодов Google Authenticator.

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

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

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

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

Простейшие методы XSS защиты

В мире веб разработки очень важно рассматривать свой код с точки зрения безопасности от взлома и инъекций.

Особенно если это работа с web запросами и(или) куками
Особенно если есть работа и запись в базу данных
Особенно если проект работает с деньгами

Работу (особенно эту “рутину”) облегчают фреймворки. В любом современной фреймворке есть методы очищающие или защищающие параметры запроса от инъекций или вредоносного кода. Их преимущества в универсальности. Их недостатки в производительности.

Кстати, один из видов вредоносного вектора атаки — это эксплуатировать время очистки параметров от скриптов (для упрощения будем называть это XSS). Т.е., злоумышленник посылает множество запросов в ту область которая будет долго обрабатывать запросы и тормозить сервер. Ведь под капотом стандартных методов защиты от XSS различные сложные регулярные выражения с заменами.

Альтернативой являются простейшие методы защиты от XSS, там, где это применимо

  1. Приведение к простейшему типу.
    Если мы знаем, что получаемый GET параметр должен быть целочисленным — мы просто можем привести его к целочисленному перед дальнейшем обработкой или же отклонять запросы, если этот параметр не целочисленный.
    Так же можно поступать, если, например, мы знаем, что получаемый параметр может принимать только определенные значения.
$cleanParam = (int)$_GET["dirtyParam"];
  1. Простые регулярные выражения, заточенные под получаемый параметр
    Обрабатываемый параметр может, например, состоять только из цифр и букв английского алфавита. Незачем “прогонять” его по всем кругам регулярно стандартной защиты от XSS. Можно написать простую регулярку.
if( preg_match("/^[a-zA-Z\d]+$/", $_COOKIE["dirtyParam"]) )