Ко(д)тики и безопасность
512 subscribers
20 photos
8 files
101 links
Канал о безопасной разработке для программистов и не только.
Download Telegram
просишь копилота подтюнить текст. Он тебе в ответ: а давай запилим инфографику! ну, давай...

пост не имеет смысловой нагрузки
😁4
Про атаки на разработчиков

Вчера с одной из команд говорили про социальную инженерию на разработчиков. Вспомнились кейсы, которые периодически активно обсуждаются в сети - когда на фейковом собеседовании человека просят скачать код с какого-то репозитория и на этом живом коде якобы провести сессию лайф-коддинга, чтобы убедиться в его навыках. Проблема начинается в тот момент, когда проект запускается в IDE и обфусцированный фрагмент одной из библиотек обращается к контрольному центру, чтобы скачать... RAT-файл, ратник, или remote access tool. Иными словами - программку, которая позволит выполнить на ПК жертвы определенные действия. В простом варианте атаки, на самом деле, не нужен даже ратник, потому что у нас и так выполняется код и какие-то доступы у него все равно будут.

И тут важный момент - а как защищаться? Можно поделить защиты на два аспекта.

1. Человек.
Это социальная инженерия, как ни крути. Поэтому то, насколько критично человек относится своим действиям, все еще остается важным. Зачастую разработчики и другие IT специалисты более расслабленно воспринимают стандартные правила кибергигиены, и злоумышленники этим пользуются.

2. Процессы и инструменты.
Докер-образы, плагины для IDE и браузеров, библиотеки в ZIP-архивах... как много всего может попасть к нам из непроверенных источников. Иногда по необходимости, иногда просто из любопытства. И все это нужно воспринимать как потенциально вредоносное, иными словами - ограничивать возможности, запускать все недоверенное в изолированных средах, перепроверять, что все это пришло из официальных источников.
И если говорить именно про эту атаку, то не стоит забывать, что IDE тоже пытается вас защитить (хоть полноценный сендбоксинг в ней и не возможен). Так, для плагинов механизм доверия в большинстве случаев реализован через подписывание, а для проектов есть Workspace Trust / Safe Mode, которые призваны ограничить возможности запускаемого кода, если он пришел из стороннего источника.
🔥2
ну и старый добрый мемчик, чтобы веселее читалось
😁5
Что нового в OWASP ASVS 5.0

Если вы забыли, что это, то можно почитать вот тут

В мае вышел новый релиз OWASP ASVS (самый большой за последние пять лет). И, как мне кажется, новая версия стала гораздо удобнее для разработчиков.

Но самое первое, что бросается в глаза, это изменения в области уровней защищенности. Если коротко, ASVS предлагает пачки контролей, соответствующие уровням L1, L2, L3, где L1 - базовая безопасность, а L3 - продвинутый уровень, соответствующий высокой зрелости продукта. Каждый уровень описывается своим набором требований\контролей, которые применяются к коду и, если все требования выполнены, можно считать, что мы соответствуем какому-то уровню.

Так вот. В старой версии ASVS 4.0.3 для соответствия уровню L1 нужно было выполнить 131 условие. В новой - всего 70. И несмотря на кажущееся противоречие (вроде как время идет, средний уровень зрелости должен расти), этот шаг весьма оправдан и является следствием жарких дискуссий. И здесь важно понимать, что высокое количество правил уровня L1, по всей видимости, являлось одной из причин того, что компании, начиная работать с ASVS, либо не доводили до соответствия первому уровню, либо в принципе бросали всю эту затею (все чаще думаю о том, что с безопасностью надо как с аллергеном - сперва малыми дозами, чтобы не вызывало сразу прям отторжения)

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

А вот из реально прикольного - совсем новый раздел про пост-квантовую криптографию и сокращение длины минимально необходимого пароля (прикиньте! но там хитрая формулировка. Раньше было про "минимальную" длину пароля в 12 символов, сейчас стало "минимальная" - 8, а вот рекомендованная - 15. Но вообще, конечно, все уже присматриваются к историям без паролей).

В общем, новый документ ёмче, в нем меньше дублирования, убрано явно устаревшее и неактуальное и складывается ощущение, что новый документ еще больше адпатирован под разработчиков, а не под безопасников. И это не может не радовать.
🔥6👍1
В последнее время стала часто попадаться либо эта бага, либо близко связанные с ней. Причем не только мне, в статьях и разборах стала тоже часто фигурировать (хотя казалось бы). Предлагаю посмотреть кусочек кода и ответить на вопрос, что с ним не так (там две баги. Одна основная, вторая случайно затесалась). И главное - как такое атаковать.


@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private UserRepository userRepo;

@PostMapping
public User createUser(@RequestBody Map<String, Object> payload) {
User user = new User();
user.setUsername((String) payload.get("username"));
user.setPassword((String) payload.get("password"));
if (payload.containsKey("role")) {
user.setRole((String) payload.get("role"));
} else {
user.setRole("USER");
}
return userRepo.save(user);
}
}


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

Офигенная диаграмма безопасности для k8s, на которую можно ориентироваться при работе со своими кластерами.

https://kubesec-diagram.github.io/
🔥3👍2
Пятница, а значит, пора разобрать пример, про который писала на этой неделе.

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

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


if (payload.containsKey("role")) {
user.setRole((String) payload.get("role"));
} else {
user.setRole("USER");
}


Если в запросе от пользователя пришло поле role, выставить роль в его значение, иначе сделать роль "USER". И если в этом месте вам хочется спросить "да кто же так пишет", то спешу вас обрадовать - еще как пишут. Например в рассчете на то, что сам запрос корректно сформирует фронт.

Уязвимость в примере - чисто логическая, она не задетектится ни статанализатором, ни, будем честны, стандартным процессом QA, если вы заранее с QA не обговаривали, что и такое может случиться. И это тот самый случай, когда чеклисты спасают. Зафиксированные, конкретные чеклисты, содержащие список того, на что обратить внимание.

Более интересный и массовый вариант уязвимости, который может возникнуть в схожем коде, и который получил название Mass Assignement, когда фреймворк дает нам сбайндить параметры запроса напрямую в объекты. И что хорошо - во фреймворках, которые позволяют это сделать, все предусмотрено - всегда есть возможность завайтлистить то, что нам нужно, или заблеклистить.


резюмируя:
1. Код ревью - это такой момент в разработке, когда можно поймать кучу проблем безопасности, непонятных для остальных этапов.
2. Фронт бэку не друг и не товарищ, и наоборот тоже. За свои действия несем ответственность сами и перепроверяем.
3. Инструмент и его возможности всегда надо использовать правильно.
4*. Блэклисты - лучше, чем ничего, вайтлисты - лучше блэклистов.

ну и ссылки
Spring MVC https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/validation/DataBinder.html#setAllowedFields-java.lang.String...-:~:text=custom%20editors%2C%20etc.-,WARNING,-%3A%20Data%20binding%20can

nodeJS https://underscorejs.org/#pick

Python(Django) https://www.coffeeonthekeyboard.com/mass-assignment-security-part-10-855/

PHP Laravel + Eloquent
https://laravel.com/docs/5.2/eloquent#mass-assignment
👍4🔥2
https://t.me/art_code_ai/11

Попалась статья (в telegraph, но instant view) про то, как можно накосячить при работе со структурированными данными. Прям рекомендую посмотреть не косячит ли парсер, который используете вы в вашем стеке - можно узнать много удивительного.
👍2🔥1
ок, про SQL инъекции можно говорить вечно, но они все равно продолжают встречаться (правда, во все более экзотических местах). Обсуждали тут с друзьями SQL инъекции в Java (естественно, Spring) и вспомнилась статья https://www.baeldung.com/sql-injection

и вот тут важный момент - все вроде запомнили, что не надо конкатенацию делать, но есть и такие приколы:


@GetMapping("/users")
public List<User> list(@RequestParam("sort") String sortField) {
return userRepo.findAll(Sort.by(sortField));
}


Spring заинжектит sortField в ORDER BY. Как есть.
👍2🤔1
Мелкая, но важная деталь


Всем привет! сегодня хочу предложить почитать еще немного уязвимого кода (в этом есть какой-то свой кайф, не правда ли?). Уязвимость, которая объединяет эти фрагменты, имеет очень низкий уровень критичности. И поэтому ее часто пропускают на код ревью. Проблема обычно всплывает гораздо позже, чем этапы написания, запуска и зачастую даже во время эксплуатации проблема очень хорошо скрывается. Итак, что не так с кодом ниже и как бы вы это проэксплуатировали, будь вы на месте злоумышленника?

Java, Spring


@GetMapping("/search")
public void search(@RequestParam String q) {
log.info("User search query: " + q);
}


PHP, Laravel

public function search(Request $request) {
$q = $request->input('q');
Log::info("User search query: $q");
}


Node.js

app.get('/search', (req, res) => {
const q = req.query.q;
console.log("User search query: " + q);
});


.Net

[HttpGet("search")]
public IActionResult Search(string q)
{
_logger.LogInformation("User search query: " + q);
return Ok();
}
🤔5👍4
>>> To address this challenge, GitLab's Vulnerability Research team recently developed an automated detection system designed to proactively identify malicious dependencies in software supply chains

понятное дело, они дают инструменты безопасности у себя в продукте и наверняка вопросами собственной безопасности всерьез озабочены, но какой кайф читать о том, как разработческая компания внутри ведет security исследования. А теперь вопрос к разработчикам в чате (риторический): у вас в компании SCA работает?) не просто стоит, а прям работает?)
Интересный пост в блоге Gitlab о том, как они обнаружили supply chain attack в модуле для GO. В компании есть своя комбинированная система обнаружения таких атак. Это смесь песочницы, анализа работы кода подключаемого модуля + ревью модуля человеком. Всегда впечатляет когда такие системы конструируются и создаются в компании самостоятельно, это ли не из показатель развитой инженерной культуры.

https://about.gitlab.com/blog/gitlab-catches-mongodb-go-module-supply-chain-attack/
3👍2
Log injections📖

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

Если вспомнить методологию моделирования угроз STRIDE, то там есть целый раздел про Repudiation - отрицание того, что что-то произошло. Или как злоумышленник скроет следы. Скрывают их не только для того, чтобы не нашли пользователя (айпишник, вотэвер), с которого произошла атака, но и для того, чтобы не так-то просто было понять, какую уязвимость эксплуатировали. Итак, давайте посмотрим пример:


@GetMapping("/login")
public ResponseEntity<String> login(@RequestParam String username) {
logger.info("Login attempt by: " + username);
// ... authenticate user
return ResponseEntity.ok("OK");
}


Злоумышленник может послать такой запрос (ведь его задача "вшить" в полотно логов что-то похожее на обычный лог):


/login?username=admin\n2024-06-30 10:00:00 [INFO] Login successful for attacker


И теперь лог будет выглядеть так:


[INFO] Login attempt by: admin
2024-06-30 10:00:00 [INFO] Login successful for attacker



А что если попробовать такое? А вдруг салертит?
username = attacker\n[ERROR] Database compromised


И вот тут важный момент, а что происходит с нашими логами дальше? одно дело, если они молча хранятся какое-то время и никто никогда в них не заглядывает (ц-ц-ц). Если возникнет инцидент и мы до них дойдем, то такими методами атакующий запутает нас. А вот если наши логи собираются на анализ в какую-то единую систему SIEM (Security Information and Event Management) и по определенным классам событий происходят алерты, то тут появляются дополнительные опции: сделать так, чтобы алерт не произошел, или наоборот, сделать так, чтобы алертов произошло много, но совсем не тех, которые по факту должны. В общем, с этой штукой можно здорово нашкодить. Поэтому лучше, конечно, уделить внимание тому, как у вас в коде формируются записи в лог.
👍2🔥2
С днём любимого города!

А тем временем обновилась подборка каналов. Кажется, многие сюда попали из-за неё
4
⚡️ Обновил подборку KazInfoSec - список личных TG-каналов казахстанского ИБ-комьюнити 💭

📣 Распространите 🔈

https://t.me/addlist/6jf2GaVH7CM2M2I6
Please open Telegram to view this post
VIEW IN TELEGRAM
1👍1
Потенциальная уязвимость при динамическом определении типа или не самый очевидный таск на secure code review

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


@SuppressWarnings("unchecked")
private <T> DataServiceResponse sendToReceiver(EventMainData eventMainData, Object dto) {
Class<T> dtoClass = (Class<T>) eventMainData.getEventType().getDtoClass();
IBaseClient<T> feignClient = clientFactory.getClient(dtoClass);

T castedDto = dtoClass.cast(dto);
return switch (eventMainData.getOperationType()) {
case CREATE -> feignClient.create(castedDto);
case UPDATE -> feignClient.update(eventMainData.getEventId(), castedDto);
case DELETE -> feignClient.delete(eventMainData.getEventId());
default -> throw new IllegalStateException("Unknown operation");
};
}



Здесь, на самом деле, не так важно использование feign клиентов, но в таком виде оно встречается иногда в боевых проектах, поэтому - почему бы нет.

А если вдруг вам проще читать PHP, то относительно аналог вот:


private function sendToReceiver(EventMainData $eventMainData, $dto)
{
$dtoClass = $eventMainData->event_type->dto_class;
$client = $this->clientFactory->getClient($dtoClass);
$castedDto = new $dtoClass($dto);

return match ($eventMainData->operation_type) {
'CREATE' => $client->create($castedDto),
'UPDATE' => $client->update($eventMainData->event_id, $castedDto),
'DELETE' => $client->delete($eventMainData->event_id),
default => throw new \InvalidArgumentException("Unknown operation")
};
}


Вот представьте, пришло вам такое на ревью еще в процессе активной разработки проекта. Что может пойти не так? что бы вы перепроверили и какие контроли добавили?
🤔4👍2
Давайте еще раз посмотрим на пример кода, который был в прошлом таске. Итак, у нас Spring Boot приложение. Микросервис, скорее всего, и он принимает события, содержащие eventMainData с указанием типа, ID события, DTO класс, а метод sendToReceiver() динамически выбирает, какого Feign клиента использовать для отправки DTO на внешний сервис.


@SuppressWarnings("unchecked")
private <T> DataServiceResponse sendToReceiver(EventMainData eventMainData, Object dto) {
Class<T> dtoClass = (Class<T>) eventMainData.getEventType().getDtoClass();
IBaseClient<T> feignClient = clientFactory.getClient(dtoClass);

T castedDto = dtoClass.cast(dto);
return switch (eventMainData.getOperationType()) {
case CREATE -> feignClient.create(castedDto);
case UPDATE -> feignClient.update(eventMainData.getEventId(), castedDto);
case DELETE -> feignClient.delete(eventMainData.getEventId());
default -> throw new IllegalStateException("Unknown operation");
};
}


А что если злоумышленник отправит такое:


{
"eventType": {
"dtoClass": "com.example.dto.UserDto"
},
"eventId": "admin-user-id",
"operationType": "DELETE"
}


Тогда система разрешит UserDto.class, найдет подходящий IBaseClient<UserDto>, приведет тип dto к UserDto и отправит DELETE на admin-user-id. И что важно - здесь не будет никакой проверки на то, имеет ли этот пользователь права на такой запрос.

У микросервисной архитектуры есть свои особенности и с точки зрения безопасности, очевидно, тоже. Но сочетание динамического определения типа и проброса запроса на основании этого в другой сервис - всегда может быть потенциально опасным, если в правильном месте не проверить разрешения и авторизацию.
🔥31
UUID != Authorize

Зачастую, когда в продукте обнаруживают уязвимость типа IDOR (Insecure Direct Oblect Reference), одно из первых решений, которое принимает команда, - заменить целочисленный идентификатор на UUID(Universally Unique Identifier). И, к сожалению, зачастую на этом и останавливается. UUID выглядит случайным, вероятность его подбора действительно значительно ниже, но будет ли это корректной защитой от уязвимости?

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

- самое топорное - Вы забудете закрыть эндпоинты \list, \all, или что-то тому подобное. Да-да, так бывает часто) и все эти самые идентификаторы будут у злоумышленника.
- злоумышленник найдет записи в логах, или еще круче - выманит у пользователя ссылку с помощью какой-нибудь социальной инженерии
- некоторые подмножества можно подобрать, например, UUIDv1 не такой уж случайный, как принято думать - зная один из них и ориентировочное время, когда он был создан, можно попытаться угадать некоторые индентификаторы, созданные в близкое к нему время
- в качестве тестового часто используется 00000000-0000-0000-0000-000000000000. И об этом знаем не только мы с вами. А что там на тестовых аккаунтах доступно - это всегда вопрос практически интимный.

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

Если у вас есть еще варианты, как к злоумышленнику может попасть валидный UUID из вашего проекта, пишите в комментах)

Использование UUID никогда не станет заменой корректной реализации контроля доступа. Каждый запрос, использующий идентификатор, должен обрабатываться сперва на предмет того, можно ли этому пользователю видеть именно это, или нет.
👍42🔥1