It is believed that the front end is only one part of the job. We need a backend to store data, we need some DevOps. Oh, I also forgot about QA. There are legends about titans from the past who could single-handedly draw a design and launch a website.
But here's the real story: we has the Great Idea, it won't work without a back-end, there are only front-end developers in the team, outsourcing is possible only in design, the deadline is too near. Just smile and wave... Or we will can realised the project uses front-end technologies only, cut comlicated things, dive into devops. Spoiler: we did it, the Bobuk-Bacek formula works, JavaScript is power.
You will find out how I Love Front-end was launched, what was cut off by Occam's razor and why everyone hates the Mona Lisa.
Audience and level.
Full-stack developers and front-end developers who want to learn how to make back-end for front-end. Just curious guys. Level from junior to middle.
11. Концепция
Делаем силами 2-3 человек + дизайнеры + редактор
Только фронтенд
-
технологии
Клиент: CRA
Бэкенд: Node.js
Задачи(загадки)
:
На чём быстрее
18. DevOps
Хостинг: VPS (так привычнее)
Флоу разработки: Trunk Based Development (так быстрее)
Автоматизация: GitHub Actions (так дешевле)
Демонизация Node.js: PM2 (так проще)
SSL
:
Certbot (быстро и бесплатно!)
35. Борис
Решил все задачи первый
Богдан
Получил ссылку от Бориса
на последний уровень
Никаких состояний в URL
36. Защита
Все уровни выдаются по одному URL (корневому)
На одном URL выдаются разные SPA
Сервер решает, какой index.html отдать
Перескочить между уровнями подменой URL невозможно
37. Где будем хранить состояние?
В базе данных — дорого
:
(
Делать авторизацию — долго
:
(
Cookie — дёшево и быстро! =)
38. Борис
Решил все задачи первый
Богдан
Получил cookie от Бориса
и попал на последний
уровень
Cookie легко сломать
39. Решение в лоб
Хранение отпечатка браузера — дорого и хрупко
:
(
Мусорные Cookie
-
обманки
Cookie
-
метка и запись в логах
40. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
41. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
42. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
43. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
44. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
45. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
46. @Get('')
getLevel(@Req() req: Request, @Res() res: Response) {
const levelCookie = req.cookies[LEVEL_COOKIE];
const userCookie = req.cookies[USER_COOKIE];
if (!userCookie) { res.cookie(USER_COOKIE, uuidv4()); }
const levelFolder = this.levelService.getLevelFolder(levelCookie);
const f
i
lepath = `${levelFolder}/index.html`;
fs.access(f
i
lepath, fs.constants.F_OK, (err)
=
>
{
fs.createReadStream(f
i
lepath).pipe(res);
})
}
Бизнес
-
логика выдачи уровня
56. location / {
if ($arg_keyword = qwerty) {
return 302 /zbfg56ffh03445561hd.html;
}
try_f
i
les $uri $uri/ =404;
root /var/
w
w
w
/tasks/mona/mona
-
main;
}
«Serverless» на Nginx
57. location / {
if ($arg_keyword = qwerty) {
return 302 /zbfg56ffh03445561hd.html;
}
try_f
i
les $uri $uri/ =404;
root /var/
w
w
w
/tasks/mona/mona
-
main;
}
«Serverless» на Nginx
58. location / {
if ($arg_keyword = qwerty) {
return 302 /zbfg56ffh03445561hd.html;
}
try_f
i
les $uri $uri/ =404;
root /var/
w
w
w
/tasks/mona/mona
-
main;
}
«Serverless» на Nginx
59. map $http_user_agent $too_new {
default 1;
"~MSIE [1-9]." 0;
"~MSIE 10" 0;
"~rv:11.0" 0;
}
location / {
if ($too_new = 1) {
rewrite ^ /browser.html redirect;
}
}
Проверка браузера на Nginx (Вход только для IE)
60. map $http_user_agent $too_new {
default 1;
"~MSIE [1-9]." 0;
"~MSIE 10" 0;
"~rv:11.0" 0;
}
location / {
if ($too_new = 1) {
rewrite ^ /browser.html redirect;
}
}
Проверка браузера на Nginx (Вход только для IE)
62. Задачи
Подсадить пользователя на крючок первых уровней
Не дать заскучать однообразными заданиями
Задержать прокачанных в CTF шустриков
Дать удовольствие от сложности
72. Самая дешёвая метрика — это логи
tail
-
f ctf
-
back
-
out.log
Наблюдаем в реальном времени
grep
-
r "flag" . | wc
-
l
Считаем взятия флага
this.logger.log(`user: ${userCookie} keys: ${keys.toString()}`);
Размечаем событие в логах