Cypress пишем первые тесты на авторизацию | by nikolay kozub | Medium

Игнорирование 2FA при восстановлении пароля

Многие сервисы выполняют автоматический вход в аккаунт после завершения процедуры восстановления пароля. Так как доступ к аккаунту предоставляется мгновенно, при входе в аккаунт 2FA может миноваться и полностью игнорироваться.

Impact аналогичного репорта на hackerone, который я прислал недавно:

Если злоумышленник получит доступ к электронной почте жертвы (он может взломать учетную запись с помощью фишинга, brute-force атаки, credentials stuffing и тд), он может обойти 2FA, хотя в этом случае 2FA должен защищать учетную запись. На данный момент для 2FA есть проверка кода Google Authenticator или резервного кода, но не кода из электронного письма, поэтому данный Bypass имеет смысл.

Ограничение скорости потоков с отсутствием блокировки после достижения определенной скорости

Зачастую исследователи безопасности пытаются подобрать код с использованием 5-и или более количества потоков, чтобы быстрее выполнить атаку (в Burp Intruder количество потоков по умолчанию- 5 без задержки). Но иногда система безопасности от перебора или обычный Load Balancer может реагировать только на этот единственный фактор.

Если вы пытаетесь брутфорсить с 5-ю потоками, стоит уменьшить количество до 1-го, а потом до 1-го с задержкой в одну секунду. Ранее мне посчастливилось наблюдать за таким поведением и именно с помощью таких манипуляций произошел успешный подбор кода, что привело к Account Takeover.

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

Безопасность

Злоумышленники могут использовать поля формы для получения информации или других вредоносных действий — это может быть сделано с помощью SQL-инъекций и вредоносных скриптов.

Отсутствие Rate-лимита

Алгоритм Rate-лимита используется для проверки возможности пользовательского сеанса (или IP-адреса) быть ограниченым в попытках или скорости, и при каких обстоятельствах это происходит. Если пользователь выполнил слишком много запросов в течение определенного промежутка времени, веб приложение может ответить 429 кодом (много запросов) или применить Rate-лимит, не показав при этом ошибок.

. Манипуляция версиями API

Если вы видите в запросе web приложения что-то вроде /v*/, где *  —  это цифра, то есть вероятность, что можно переключиться на более старую версию API. В старой версии API может быть слабая защита или таковой может вовсе не быть. Это довольно редкое явление и возникает в том случае, если разработчики забыли удалить старую версию API в production/staging среде.

Генерируемый OTP код не изменяется

Это касается не постоянно изменяющихся кодов как в Google Authenticator, а только статичных, которые приходят в SMS, email или личным сообщением в мессенджере.

Суть данного обхода состоит в том, что постоянно или в течении некоторого времени, например, 5 минут, в SMS отправляется один и тот же OTP код, который в течении всего этого времени является валидным. Так же стоить следить за тем, чтобы не произошел silent rate-limit.

Если 2FA крепится к IP-адресу, то можно попытаться его подменить

Чтобы идентифицировать данный метод, войдите в свой аккаунт с помощью функции запоминания 2FA, потом перейдите в другой браузер или incognito режим текущего браузера и попробуйте войти снова. Если 2FA не запрашивается вовсе, значит произошло крепление 2FA к IP-адресу.

Игнорирование 2FA при входе через соцсеть

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

Impact одного из репортов:

Rate лимит существует, но его можно обойти

Кейсы, которые раньше приходилось встречать:

Стабильность

Когда пользователь вводит данные, которые не могут быть обработаны приложением, оно может среагировать неожиданным образом — например, упасть.

Сброс rate-limit-a при обновления кода.

В запросе проверки кода, rate-лимит присутствует, но после задействования функционала повторной отправки кода он сбрасывается и позволяет продолжать брутфорс кода.


Примеры репортов:

Корректное поведение UI и единообразие

Очень важно, чтобы все формы в приложении были единообразны (и по внешнему виду, и по поведению).

Обход 2фа с помощью подстановки части запроса из сессии другого аккаунта

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

Например, при отправке OTP-кода проверяется ID формы, ID пользователя или cookie, которое связано с отправкой кода. Если применить данные с параметров аккаунта, на котором нужно обойти code-верификацию (Account 1), на сессию совсем другого аккаунта(Account 2), получим код и введем его на втором аккаунте, то сможем обойти защиту на первом аккаунте. После перезагрузки страницы 2FA должна исчезнуть.

Игнорирование 2FA в случае кроссплатформенности


Имплементации 2FA в мобильной или десктопной версии могут отличаться от web версии приложения. 2FA может быть слабее, чем в web версии или вовсе отсутствовать.

7. При отключении 2FA не запрашивается текущий код.

Если при отключении 2FA не запрашивается дополнительное подтверждение, такое как текущий код с google authenticator приложения, код с email/телефона, то в таком случае имеются определенные риски. При чистом запросе существует вероятность CSRF атаки. Если будет найден вектор обхода CSRF защиты (если она есть)

, то 2FA можно будет отключить. Также может использоваться clickjacking уязвимость, — после пары кликов от ничего не подозревающего пользователя 2FA будет отключена. Подтверждение предыдущего кода добавит дополнительную защиту 2FA, учитывая потенциальные CSRF/XSS/Clickjacking атаки, а также CORS misconfigurations.

Обход rate-лимита путем смены IP адреса

Множество блокировок основаны на ограничении приема запросов с IP, который достиг порога определенного количества попыток при выполнении запроса. Если IP-адрес сменить, то есть возможность обойти это ограничение. Для того, чтобы проверить данный способ, просто смените свой IP с помощью Proxy-сервера/VPN и увидите, зависит ли блокировка от IP.

Похожее:  Системы контроля и управления доступом / Хабр

Способы смены IP:

Так как IP rotate тулза отправляет запросы с помощью AWS IP-адресов, все запросы будут блокироваться, если веб приложение находится за CloudFlare фаерволом.

В данном случае нужно дополнительно обнаружить IP оригинального веб сервера или найти способ, не касающийся AWS IP-адресов.

Обход 2FA с помощью «функционала запомининания»

На многих сайтах, поддерживающих 2FA авторизацию, есть функционал «запомнить меня». Он полезен в том случае, когда пользователь не желает вводить 2FA код при последующих входах в аккаунт. Важно идентифицировать способ, с помощью которого 2FA «запоминается». Это может быть cookie, значение в session/local storage или просто крепление 2FA к IP адресу.

Чистота базы данных

Форма — место, которое наполняет базу данных. Если есть проблемы с валидацией, появляется угроза того, что некорректные данные попадут в БД. Это ведет к проблемам с поведением приложения и потенциальным багам.

На сайте включена поддержка X-Forwarded-For

Встроенный header X-Forwarded-For может использоваться для смены IP. Если в приложение встроена обработка данного хедера, просто отправьте X-Forwarded-For: desired_IP для подмены IP, чтобы обойти ограничение без использования дополнительных прокси. Каждый раз, когда будет отправлен запрос с X-Forwarded-For, веб-сервер будет думать, что наш IP адрес соответствует значению, переданному через хедер.

Материалы на эту тему:

Improper access control баг на странице ввода 2FA

Иногда страница-диалог для ввода 2FA представлена в виде URL с параметрами. Доступ к такой странице с параметрами в URL с cookies, которые не соответствуют тем, которые использовались при генерации страницы или вообще без cookies, —  это не безопасно. Но если разработчики решили принять риски, то нужно пройтись по нескольким важным пунктам:

  1. истекает ли ссылка для ввода 2FA;
  2. индексируется ли ссылка в поисковиках.

Игнорирование 2FA при определенных обстоятельствах


При выполнении некоторых действий, которые приводят к автоматическому входу в аккаунту, 2FA может не запрашиваться.

Отсутствие Rate-limit-а в личном кабинете

2FA может внедряться в различные функционалы личного кабинета пользователя для большей безопасности. Это может быть изменение email адреса, пароля, подтверждение изменения кода для осуществления финансовых операций, etc. Наличие rate-limit-a в личном кабинете может отличаться от наличия rate-limit-a в 2FA при входе в аккаунт.

Если разработчики изначально добавили защиту против несанкционированного изменения данных, то данную защиту нужно поддерживать и исправлять все возможные bypass-ы. Если bypass найден, то это расценивается как уязвимость обхода «security feature», которая была имплементирована разработчиками.

Cypress пишем первые тесты на авторизацию

npm install cypress --save-dev
или yarn
yarn add cypress --dev
cy.intercept('http://example.com/widgets',{ fixture:'widgets.json'})
describe(‘Test describe’, () => {
it(‘should visit login page’, () => {
cy.visit(‘ http://localhost:4200/')
})
})
// cypress.json
{
“baseUrl”: “http://localhost:4200"
}
describe('Test describe', () => {
it('should visit login page', () => {
cy.visit('/login')
cy.getEl('register').click()
cy.url().should('include', '/register')
})
})
cy.getEl(‘register’).click()
Cypress.Commands.add('getEl', name => cy.get(`[data-cy="${name}"]`))
describe('auth', () => {
it('fill all the gaps', () => {
cy.visit('/register')
cy.getEl('firstName').type('Nikolay');
cy.getEl('lastName').type('Kozub');
cy.getEl('username').type('mk');
cy.getEl('password').type('qwerty');
})
})
describe('auth', () => {
it('fill all the gaps', () => {
cy.visit('/register')
cy.getEl('firstName').type('Nikolay');
cy.getEl('lastName').type('Kozub');
cy.getEl('userName').type('mk');
cy.getEl('password').type('qwerty');
cy.getEl('register').click()

cy.getEl('loader').should('be.visible')
cy.getEl('loader').should('not.exist')
cy.url().should('include', '/login');
cy.getEl('alert').should('contains.class', 'alert-success');
cy.getEl('alert').should('contain', 'successful');
})
})

export const user = {
name: 'firstName',
lastName: 'lastName',
userName: 'username',
pass: 'password',
regBtn: 'register',
loader: 'loader'
}

export const alertTab = {
alert: 'alert'
}

import {alertTab, user} from "../support/locators";

describe('User', () => {
it('fill oll the gaps', () => {
cy.visit('/register')
cy.getEl(user.name).type('Nikolay');
cy.getEl(user.lastName).type('Kozub');
cy.getEl(user.userName).type('mk');
cy.getEl(user.pass).type('qwerty');
cy.getEl(user.regBtn).click()

cy.getEl(user.loader).should('be.visible')
cy.getEl(user.loader).should('not.exist')
cy.url().should('include', '/login');
cy.getEl(alertTab.alert).should('contains.class', 'alert-success');
cy.getEl(alertTab.alert).should('contain', 'successful');
})

import {alertTab, user, authLocators} from "../support/locators";

describe('User', () => {
const {loader, regBtn} = authLocators
const {alert} = alertTab

it('fill all the gaps', () => {
cy.visit('/register')
Object.values(user).forEach(el => cy.getEl(el.key).type(el.value))
cy.getEl(regBtn).click()

cy.getEl(loader).should('be.visible')
cy.getEl(loader).should('not.exist')
cy.url().should('include', '/login');
cy.getEl(alert).should('contains.class', 'alert-success');
cy.getEl(alert).should('contain', 'successful');
})
})

Похожее:  РН банк личный кабинет: вход в систему и регистрация
{
"baseUrl": "http://localhost:4200",
"username": "mk",
"password": "qwerty"
}
export const user = {
firstName: 'Nikolay',
lastName: 'Kozub',
username: Cypress.config('username'),
password: Cypress.config('password'),
}
Cypress.Commands.add('setItem', (keyName, data) => localStorage.setItem(keyName,
JSON.stringify(data),
)
)
export const loginUserData = (firstName, lastName, password, username,idUser = 1) => ([
{
firstName,
lastName,
username,
password,
"id": idUser
}
])
<input type="text" formControlName="username" data-cy="loginName" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.username.errors }" />
<input type=”password” data-cy=”loginPass” formControlName=”password” class=”form-control” [ngClass]=”{ ‘is-invalid’: submitted && f.password.errors }” />
<button [disabled]="loading" class="btn btn-primary" data-cy="login">Login</button>
<h1 data-cy="userName">Hi {{currentUser.firstName}}!</h1>
export const authLocators = {
regBtn: 'register',
loader: 'loader',
loginBtn: 'login',
loginName: 'loginName',
loginPass: 'loginPass'
}
export const homePage = {
userName: 'userName'
}
import { authLocators, user, homePage } from '../support/locators'
import { loginUserData } from '../fixtures/user'
const { name, lastName, userName, pass } = userdescribe('login', () => {before(() => {
cy.setItem('users', loginUserData(name.value,lastName.value,userName.value, pass.value))
cy.visit('/login')
})

it('should login user page', () => {
cy.getEl(authLocators.loginName).type(Cypress.config('username'))
cy.getEl(authLocators.loginPass).type(Cypress.config('password'))
cy.getEl(authLocators.loginBtn).click()
cy.getEl(homePage.userName).should('contain.text', name.value)
})
})

cy.intercept('http://example.com/settings').as(‘alias’)
cy.wait(‘@alias’)
import { authLocators, user, homePage } from '../support/locators'
import { loginUserData } from '../fixtures/user'

const { name, lastName, userName, pass } = user

describe('login', () => {
before(() => {
cy.setItem('users', loginUserData(name.value, lastName.value, userName.value, pass.value))
cy.visit('/login')
cy.getEl(authLocators.loginName).type(Cypress.config('username'))
cy.getEl(authLocators.loginPass).type(Cypress.config('password'))
cy.intercept('GET', '**/posts').as('alias')
cy.getEl(authLocators.loginBtn).click()
})

it('should login user page', () => {
cy.wait('@alias').then(req => {
const { body } = req.response
const firstTenItems = body.splice(0, 10).map(el => el.title)

firstTenItems.forEach((el, idx) =>
cy.getEl(homePage.listItem)
.eq(idx)
.then(element => expect(element.text()).contains(el)),
)
cy.getEl(homePage.userName).should('contain.text', name.value)
})
})
})

Cypress.Commands.add('login', () => {
cy.request({
method: 'POST',
url: '/login',
body: {
username: Cypress.config('username'),
password: Cypress.config('password')
}
}).then((response) => {
localStorage.setItem('token', response.body);
});
});

Анализ граничных значений

Кроме классов эквивалентности, нужно протестировать граничные значения. Граничные значения это:

  • Минимальное значение
  • Максимальное значение
  • Выборочные значения из промежутка

Вот пример:

Граничные значения проверяются, потому что ни один разработчик не застрахован от так называемой ошибки на единицу.

Бонус: bugmagnet

Для тестирования форм есть отличный плагин для Chrome и Firefox — BugMagnet. После установки кликните правой кнопкой мыши по любому полю формы и у вас появится возможность выбора значений для заполнения из огромного списка данных. Вот видео, на котором показано, как работаем BugMagnet:

Дополнение

Вместе со всем вышеперечисленным нужно попробовать:

Разделение эквивалентности

Разделение эквивалентности — это разделение набора возможных данных для ввода на классы. Каждый член класса считается идентичным всем остальным.

Похожее:  Личный кабинет Твинго: регистрация и вход

Давайте разберем это на примере. Предположим, что есть поле для ввода, которое может принимать только числовые значения в промежутке от 1 до 200 000. Вот классы эквивалентности, которые нужно будет проверить:

  • Числа меньше минимального значения промежутка (меньше 1)
  • Числа внутри промежутка (1-200 000)
  • Числа больше максимального значения промежутка (больше 200 000)

Таблица верификации полей

https://www.youtube.com/watch?v=2TvLwIQQFA0

Вот пример таблицы для верификации полей ввода. Вы можете составить такую же для вашей формы.

Тип данныхКорректный вводНекорректный ввод
Положительные целые числа— только числа
— максимально возможное значение (N)
— числа внутри промежутка (N 1) / 2
— число больше максимально возможного (N 1)
— дробные числа
— отрицательные числа
— строковые значения
— числа строковые значения
— числа специальные символы
— Unicode (например U 0000, U 0001)
Строки— только символы
— только числа
— только специальные символы
— числа символы
— числа спецсимволы
— символы спецсимволы
Даты— проверить, что при выборе появляется datepicker
— проверить, что поле нельзя изменить с клавиатуры
— проверить, что значение поля можно скопировать, но вставить нельзя
— проверить, что при выборе значения в datepicker, оно появляется в поле
— проверить, что в феврале 29 дней в високосных годах
— проверить, что в феврале 28 дней не в високосных годах

Тестируемый компонент: авторизация на сайте — мегалекции

Составить тест-кейсы на тестирование формы авторизации на страничке.

Форма авторизации имеет:

· поле ввода электронной почты (электронная почта служит логином для авторизации, поддерживает по документации до 20 символов);

· поле ввода пароля (поддерживает по документации до 20 символов);

· чекбокс «Запомнить почту и пароль» (по умолчанию неактивен);

· кнопку «Авторизироваться».

После успешной авторизации пользователь должен увидеть сообщение “Всё окей”. В случае неудачной авторизации пользователь должен увидеть сообщение “Извини, не в этот раз”.

Комментарии к спеку:

· не указано в спеке до 20 символов в поле ввода электронной почты и пароля: включительно или нет? (я взял не включительно, то есть максимум 19 символов);

· не указано минимально допустимое количество символов в поле ввода электронной почты и пароля (создал тест-кейс для тестирования пустых полей ввода);

· не указано какие символы допустимы в пароле;

· не указано название сайта (я принял его за «example.ru»);

· не указаны координаты расположения формы авторизации на веб-странице;

· при необходимости можно дополнить тест-комплект тест-кейсами на проверку клавиш «Backspace» и «Escape».

Среда выполнения: Google Chrome 41.0.2272.101 m.

Тестируемый компонент: Авторизация на сайте

Комментарий: Используй Логин=Auth.t@yandex.ru и Пароль=123456789Abcdef для успешной авторизации на сайте.

Номер тест-кейса:008 Названия тест кейса: Проверка поля ввода пароля на количество знаков
Предусловие 1) перейти на сайт; 2) если вы уже авторизированы – выйдите из вашего аккаунта; 3) галочка снята в чекбоксе «Запомнить почту и пароль»
№ шага Шаги к воспроизведению Ожидаемый результат
Ввод в поле ввода пароля «123456789Abcdefghij» – 19 знаков Значение в поле ввода пароля (19 знаков):*******************
Ввод в поле ввода пароля «123456789Abcdefghijk» – 20 знаков Значение в поле ввода пароля (19 знаков):*******************
Ввод в поле ввода пароля «123456789Abcdefghijklmno» – 24 знаков Значение в поле ввода пароля (19 знаков):*******************
Окончание тест-кейса
Номер тест-кейса:009 Названия тест кейса: Проверка полей ввода электронной почты и пароля на математические символы, знаки пунктуации, пробел
Предусловие 1) перейти на сайт; 2) если вы уже авторизированы – выйдите из вашего аккаунта; 3) галочка снята в чекбоксе «Запомнить почту и пароль»
№ шага Шаги к воспроизведению Ожидаемый результат
Ввод в поле ввода пароля «#№!;$%:^&?*(@») =*/ .->» – 24знака Значение в поле ввода пароля (19 знаков):*******************
Ввод в поле ввода электронной почты ««#№!;$%:^&?*(@») =*/ .->» – 24 знака Значение в поле ввода электронной почты (19знаков): #№!;$%:^&?*(@») =*/
Ввод в поле ввода пароля «#№!;$%:^&?*(@») =*/ » – 20 знаков Значение в поле ввода пароля (19 знаков):*******************
Ввод в поле ввода электронной почты «#№!;$%:^&?*(@») =*/ » – 20 знаков Значение в поле ввода электронной почты (19знаков): #№!;$%:^&?*(@») =*/
Ввод в поле ввода пароля «#№!;$%:^&?*(@») =*/» – 19 знаков Значение в поле ввода пароля (19 знаков):*******************
Ввод в поле ввода электронной почты «#№!;$%:^&?*(@») =*/» – 19 знаков Значение в поле ввода электронной почты (19знаков): #№!;$%:^&?*(@») =*/
Кликнуть левой клавишей мыши по кнопке «Авторизироваться» 1) Авторизация не выполнена
2) Сообщение на экране «Извини, не в этот раз»
Окончание тест-кейса
Номер тест-кейса:013 Названия тест кейса: Проверка авторизации при смене раскладки клавиатуры
Предусловие 1) перейти на сайт; 2) если вы уже авторизированы – выйдите из вашего аккаунта; 3) галочка снята в чекбоксе «Запомнить почту и пароль»
№ шага Шаги к воспроизведению Ожидаемый результат
Ввод в поле ввода электронной почты «Фгерю»нфтвучюкг» Значение в поле ввода электронной почты: Фгерю»нфтвучюкг
Ввод в поле ввода пароля «123456789ФИСВУА» Значение в поле ввода пароля:***************
Кликнуть левой клавишей мыши по кнопке «Авторизироваться» 1. Авторизация не выполнена
2. Сообщение на экране «Извини, не в этот раз»
Окончание тест-кейса
Номер тест-кейса:014 Названия тест кейса: Проверка максимального количества неуспешных попыток авторизации
Предусловие 1) перейти на сайт; 2) если вы уже авторизированы – выйдите из вашего аккаунта; 3) галочка снята в чекбоксе «Запомнить почту и пароль»
№ шага Шаги к воспроизведению Ожидаемый результат
Ввод в поле ввода электронной почты «Фгерю»нфтвучюкг» Значение в поле ввода электронной почты: Фгерю»нфтвучюкг
Ввод в поле ввода пароля «123456789ФИСВУА» Значение в поле ввода пароля:***************
Кликнуть левой клавишей мыши по кнопке «Авторизироваться» 1.Авторизация не выполнена
2.Сообщение на экране «Извини, не в этот раз»
Ввод в поле ввода электронной почты «Фгерю»нфтвучюкг» Значение в поле ввода электронной почты: Фгерю»нфтвучюкг
Ввод в поле ввода пароля «123456789ФИСВУА» Значение в поле ввода пароля:***************
Кликнуть левой клавишей мыши по кнопке «Авторизироваться» 1.Авторизация не выполнена
2.Сообщение на экране «Извини, не в этот раз»
Ввод в поле ввода электронной почты «Фгерю»нфтвучюкг» Значение в поле ввода электронной почты: Фгерю»нфтвучюкг
Ввод в поле ввода пароля «123456789ФИСВУА» Значение в поле ввода пароля:***************
Кликнуть левой клавишей мыши по кнопке «Авторизироваться» 1.Авторизация не выполнена
2.Сообщение на экране «Извини, не в этот раз».
3. Сообщение на экране «Вы превысили максимально возможное число попыток авторизации. Ваш аккаунт заблокирован. Чтобы разблокировать аккант проверьте вашу почту».
Окончание тест-кейса

Воспользуйтесь поиском по сайту:

Cypress пишем первые тесты на авторизацию | by nikolay kozub | Medium

1 Звезда2 Звезды3 Звезды4 Звезды5 Звезд (1 оценок, среднее: 4,00 из 5)
Загрузка...

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *