Как стать автором
Обновить

Охотничьи истории

Время на прочтение10 мин
Количество просмотров4.1K
Василий Перов. "Охотники на привале". 1871 г.
Василий Перов. "Охотники на привале". 1871 г.

Навеяно вот этим постом. Люблю этот жанр, прочитал и вспомнил свои истории, захотелось поделиться. Не везде был виноват именно я, но в той или иной степени участвовал. Надеюсь, получилось интересное пятничное чтиво.

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

1. О пользе валидации данных

В первой половине 90-х писал я реестр акционеров для довольного большого по тем временам холдинга. Народ активно нес денежки под обещание 60% годовых (они и правда были, не кидалово), а чтобы сделать это еще более привлекательным, была объявлена лотерея - обладатель акции с выигравшим номером получал значительную сумму, не помню точно сколько, но помню, что прилично, квартиру можно было купить.

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

И вот уже когда финал был близок, оставалось ввести всего ничего и я уже мысленно собирался домой, на подгибающихся ногах входит одна из наших операторов и в состоянии, близком к истеричному, говорит, что она сделала что-то не то. Я бегу смотреть, и выясняется, что она случайно перепутала поля ввода начального номера и количества, то есть получалось, что клиент вместо пары акций купил десятки, если не сотни тысяч. По тому, как компьютер задумался, хотя раньше все срабатывало моментально, она поняла, что косяк, и сразу вырубила комп (да, логика была в клиенте, на сервере только данные), и успели перетереться данные по небольшому количеству акций, может пара тысяч (каждая акция - отдельная запись в БД, такая модель данных была), но надо было найти правильную информацию по бумажным записям и вбить все руками. Я сидел и диктовал оператору данные, она вбивала. Разошлись под утро. Никого не побили, хотя я имел, что слушать (и справедливо, конечно).

2. О пользе тестирования в боевой обстановке

Эта история произошла сразу после первой, на следующее же утро. Тут надо сделать маленькое отступление относительно архитектуры моего приложения - вся логика, как я уже говорил, была в клиенте, а данные хранились на сервере, в Btrieve, это такой ISAM, встроенный в NetWare, очень быстрый. У Btrieve было два варианта - для NetWare (сетевой) и для DOS (персональный), и важно отметить, что формат файлов данных был общий, так что план был такой - взять данные сервера, перекинуть на ноут, и там локально по ним искать, благо приложение умело работать и с локальным Btrieve'ом тоже (весь процесс был неоднократно протестирован). Уже догадались, что будет дальше?

Начиная с какой-то версии Novell бросил поддержку DOS-овской версии Betrieve, и незадолго до этого мы апгрейднули NetWare, ну и Btrieve с ним. Поэтому когда я увидел сообщение о поврежденном файле данных, я сразу понял, что при апгрейде Btrieve изменил формат файлов, и локально все это работать не может. Где-то на телевидении осветители уже прогревали софиты, чтобы показать всему миру розыгрыш счастливой акции, не зная о том, что розыгрыша не будет, потому что мы не сможем сходу найти, кому принадлежит выпавший номер. Я понял, что это мой последний рабочий день, сел на стол и закурил, прямо в офисе, задумчиво глядя в окно.

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

3. О в̶р̶е̶д̶е̶пользе начальника с крепкой рукой

Несколько крупных финансовых организаций решили замутить валютный аукцион. Моя контора внесла свою долю софтом, который наш отдел разработал по требованиям, полученным от руководства. Система получилась любо-дорого - сетевая, многопользовательская, модные TurboVision'овские интерфейсы, данные опять же в Btrieve, клиенты кидают серверу уведомления по SPX, данные выводятся на бегущую строку, автоматически печатаются красивые подтверждения сделок (сколько мы бились с PCL5e!). И работало шустро. Для тех времен, когда почти все писалось на Clipper'е или FoxPro, это был космос.

Наступает день первого аукциона. Обрудование смонтировано, бегущая строка переливается цифрами, в принтер заложена дорогая бумага с водяными знаками - круто до невозможности. За полчаса до начала подходит большой босс и говорит, что руководители банков сейчас перетерли и решили поменять принцип проведения аукциона. Я не помню точно, в чем заключалось изменение, но главное - оно не ложилось в ту логику, которую я запрограммировал. Я сказал, что за полчаса до запуска ничего менять не буду, и уже было собрался сесть на стол и закурить, но тут почувствовал на плече крепкую руку своего непосредственного начальника, которой он вдавливал меня в стул, и услышал его злобный голос: "показывай код!". Я не знаю, зачем я взял с собой дискеты с исходниками и компилятором, наверно чутье. Я быстро проинсталлировал компилятор, открыл код, мы вместе с шефом посмотрели код, и решили, что самое простое и надежное будет добавить в одном месте goto. Да, goto. В программу на Паскале! Хорошо, что в этот момент рядом не было старика Вирта.

Уже догадались, что будет дальше? А вот и нет! Все отработало, как надо. Вот такой случился полный Agile в то время, когда еще и слова такого не было.

4. О том, что программисты могут в железо

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

И вот как-то раз мой коллега, который занимался банкоматным железом, рассказал мне про один проблемный банкомат, который постоянно теряет связь, но проблема явно не железная, так что мне надо ехать разбираться. Надо - значит надо, на следующее утро мы с ним сели в машину и поехали, 150 км до банкомата. Приезжаем - банкомат off-line. Вызываем инкассацию, ждем их, они забирают деньги и оставляют нам открытый банкомат. Я на всякий случай переустанавливаю весь софт, созваниваюсь с оператором X.25 сети, сверяем все коммуникационные параметры, все хорошо, связь есть. Ждем полчаса, час - связь есть. Вызываем инкассацию, они заряжают деньги, вводят ключи шифрования в банкомат, закрывают банкомат.

Мы выходим из отделения банка и садимся напротив перекусить. Пока ждем свой заказ, я замечаю, что картинка на экране банкомата перестает меняться. Подхожу ближе - точно, off-line. Звоню оператору - да, связь пропала с нашей стороны. Обед отменяется, возвращаемся в отделение, вызываем инкассацию, они забирают деньги, оставляют нам открытый банкомат. Через какое-то время связь магическим образом восстанавилась, сама! Но что поменялось? Не могут же кассеты с деньгами влиять, в самом деле! Может, дверь? Закрываю дверь - через пять минут связь пропадает. Открываю - восстанавливается. Уже что-то. Исследуем дверь - ничего она не пережимает, ну никак. На что еще может влиять закрытая дверь? Температура, влажность. Может, где-то плохой контакт, холодная пайка? Поскольку диагностика ни на что не жалуется, скорее всего проблемы уже где-то совсем на выходе, может, плохо припаяны провода к RS-232 разьему? Я дергаю за пучок проводов, и один провод отходит. Припаиваем провод, закрываем дверь, ждем - связь есть! Снова вызываем инкассацию, они уже конкретно недовольные приезжают, заряжают деньги и смотрят так злобно, достали мы их, а я не люблю доставать суровых дядек с оружием, так что обещаю им, что это в последний раз, и надеюсь, что это правда.

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

P.S. Кстати, по поводу кассет - как-то раз я заходил к клиенту, и клиент попросил меня забрать пару сломанных кассет, отдать коллегам. Иду я по улице, несу кассеты, и слышу, как сзади пацаны мелкие переговариваются, и один другому говорит: "это кассеты для банкомата!". И тут до меня дошло, что я один иду по улице с кассетами, окружающие догадываются, что это кассеты, но не знают, что они пустые. А время было суровое, середина 90-х. Но ничего, дошел до офиса, и кассеты донес.

5. О том, что программисты не (всегда) могут в железо

Была такая карточная сеть - Europay, европейский оператор MasterCard, ну и свои карты у них тоже были. Для подключения к их сети использовался Евромодуль на базе IBM Series/1, очень древней мини-машины. Обслуживал этот Евромодуль с десяток крупных местных банков, так что основной поток транзакций по картам MasterCard/Eurocard/Maestro шел через него. И надо было в этом Евромодуле что-то поменять, кажется, диск поставить побольше, и сделать это надо было ночью, когда транзакций мало.

Почему-то менять диск послали меня, при том что у нас было три чистых железячника. Наверно, просто потому, что я был самый молодой, и мне было нелениво пойти в 3 часа ночи в датацентр. Пришел. Отключили Евромодуль, поменял я диск, включаю по инструкции (это вообще привет из 70-х - сначала включаешь диск, по звуку определяешь, что он раскрутился, потом включаешь системный блок) - на панели код ошибки. По справочнику ошибок смотрю - проблема с сетевым интерфейсом. Вытаскиваю карту "моего любимого" X.25, засовываю назад - Евромодуль начинает выкидывать ошибку, что вообще карта не обнаруживается. Пару часов я еще помучился, пока из банков не стали звонить и спрашивать, что происходит. Тогда чуваки из датацентра сказали, чтобы я шел на фиг, они сами будут разбираться. Как я понял, они заказали в Europay новую карту, и им ее в тот же день прислали, но менял ее уже не я. В любом случае весь день транзакции по картам не проходили.

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

6. О том, что никогда не знаешь, откуда может прилететь

С распространением карт и банкоматов попер фрод, это неизбежно. И вот как-то раз банк A поймал кардера, который в их банкомате снимал деньги. Бывает. Пикантности этой истории добавило то, что им оказался работник карточного отдела банка Б, человек, который в этом самом банке Б заведовал банкоматами.

Мне потом удалось по крупицам восстановить картину, и выглядела схема примерно так - этот работник занимался установкой банкоматов, в том числе вводил банкоматные ключи, ну и как-то раз у него остались в руках оба компонента ключа. Поскольку он занимался банкоматами, он то ли в логах (это было задолго до введения PCI DSS, запрещавшего сохранять PIN-блоки хоть в каком виде), то ли просто сниффером читал весь банкоматный трафик, там он смог найти транспортный ключ, и им уже расшифровать PIN-блоки из того же трафика. Ну и содержимое магнитной полосы, самой собой. Дальше он нашлепал несколько карт и отправился к банкомату банка А, прямо на той же самой улице, так что не исключаю, что его просто по камерам отследили - они в том районе густо понатыканы.

Как эта история касалась меня? К сожалению, напрямую. Банк Б был моим клиентом, и я этого парня обучал, рассказывал ему про протокол Diebold 912 (протоколов общения банкоматов с контроллером, который они использовали), про возможные атаки и способы их предотвращения, так что вполне возможно, что идею он почерпнул из моих рассказов и документации, которую я ему давал. Потом у меня был долгий разговор с руководителем ИТ банка - он хотел выяснить, не был ли я причастен к этой истории. Было очень неприятно.

7. О пользе внимательности

Потом я работал в другой конторе, там мы писали софт для карточного процессинга и для управления банкоматами. И был у нас TCP/IP-шный драйвер, который использовался для связи с банкоматами, и был он глюковат. Сети в те времена были ненадежные, связь часто терялась, при этом терялась плохо, без посылки RST, и получали мы классический half-open.

Чтобы обрабатывать такие ситуации, драйвер после accept'а опять запускал listener (по некоторым причинам первый listener мы закрывали), и когда приходил новый коннект, проверял IP адрес - если это банкомат, то старый сокет закрывался, и использовался новый. В теории должно работать, и на всех юнит-тестах работало, как надо. Но в составе системы - не работало. Я взялся разобраться с этой проблемой, и помню, что наворотил неслабо, фактически продублировал работу listen queue в приложении (потому что в kernel'е она, похоже, не работала так, как нам надо), и на каждый слот этой очереди выделялась структура в памяти. В тестовой среде с одним банкоматом все замечательно отработало. Ставим драйвер клиенту, и моментально от клиента прилетают матюги - каждый драйвер сжирает около 3 мегов памяти, а драверов запускается по количеству банкоматов, то есть сотни. На дворе был 2000 год или около того, то есть нормальный объем памяти у приличного HP 9000 L-класса был в районе 1-2 гигов, а тут 200 банкоматов сразу отожрали 600 мегов. Сел разбираться, и оказалось, что банкоматная подсистема, когда общается с TCP/IP-драйвером, передает параметры в неправильном порядке, и значение таймаута в 10000 миллисекунд попадает в параметр длины очереди, и драйвер выделяет память под 10000 структур, как просили. И разрешилась загадка с оригинальным драйвером - когда он использовал такой огромный размер очереди, HP'шный kernel не возвращал ошибку, но и входящие коннекты не принимал.

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

8. О пользе проверки размеров буфера

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

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

Дело в том, что номер карты состоит из 6-значного префикса (он называется BIN), 9 произвольных цифр и одного контрольного разряда. BINы стоят денег, поэтому их стараются использовать экономно, и несколько первых цифр из 9 произвольных используют как код продукта (разные продукты могут иметь разный набор разрешенных операций, лимиты, комиссии, да даже авторизовываться по-разному), таким образом обычно выделяют карточный префикс, который состоит из BIN'а и 2-3-значного кода продукта, оставляя 6-7 цифр произвольных. По этому префиксу карточная система идентифицирует продукт, и определяет, как обрабатывать транзакцию с этой картой, и делает это некий внутренний рутер.

Так вот, как рассказал мне бывший коллега, тот, кто писал этот рутер, выделил массив в 9 байт под карточный префикс, на тот случай, если банк настроит для BINа 3-значный код продукта, и все замечательно работало. А потом в какой-то момент банк для нового BINа задал 4-значный код продукта, то есть префикс стал 10 знаков, и в буфер уже не влезал. А проверку тот ленивый программист не сделал, потом что ну кому же придет в голову делать такой длинный код продукта! При первой же транзакции с картой с новым длинным префиксом рутер крэшнулся, а без него ничего не работает вообще.

Ну, вы поняли, кто был тем рукожопом. Мне было очень стыдно. Пожалуй, лучший момент для совершения сеппуку, будь я японцем.


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

Теги:
Хабы:
+31
Комментарии8

Публикации