É bom que haja alguém mais experiente na equipe que mostre o que e como fazer, o que ajuntar e inclinar o que e onde baixar os melhores desenhos de bicicletas para 2007 em DVD. Esta história é sobre como o desejo foi dado como válido, qual foi o resultado e como a crise foi superada.
Isso aconteceu no momento em que, tendo-me parecido uma experiência medíocre no desenvolvimento, eu estava procurando um lugar onde você pudesse evoluir (ou mudar) de um não-júnior para um júnior confiante. Nos caminhos misteriosos do Senhor, esse lugar foi encontrado, um projeto foi anexado ao local e o programador da “velha escola”, que escreveu mais de meninas sobre sua carreira em sistemas. “Ótimo! O projeto e, portanto, há dinheiro para a RFP, o mentor está anexado, nós vivemos! ” Eu pensei, mas então, como na descrição de um horror típico, os heróis na escuridão enfrentaram um horror terrível ...
Primeiras coisas primeiro:
1. O tamanho importa
Iniciamos o desenvolvimento em um mecanismo php escrito uma vez, usado para armazenar dados (aqui você pode pensar em MySQL \ PostgreSQL \ SQLite \ MongoDB \ Algo-lá-mas-necessariamente-com-sufixo DB-caso contrário- caras-não entendo, mas eles não adivinharam) api-gateway.
“Haha, usando php, você anexa outro gateway API a ele e armazena dados nele? Não é mais fácil trabalhar com a API diretamente do código js? Ou usa DBMS + PHP? ” - pergunte ao leitor experiente. E ele estará certo. Mas naquela época, eu, que ainda não tinha visto a espécie, não pensava assim, bem, quem sabe, caras legais provavelmente fazem isso, e os programadores da “velha escola” sabem melhor.
Como fui explicado mais adiante:
- Gateway = segurança, ninguém entrará e sairá assim
- Gateway = armazenamento seguro de dados, você não pode entrar nele + backups
- Gateway = velocidade, funciona rapidamente e sem falhas, testado pelo tempo
- O ponto de vista autoritário dos programadores da “velha escola” é que o seu php está cheio de falhas, qualquer aplicativo da Web é hackeado por padrão, portanto não há nada para armazenar dados próximos a ele
Uma característica do gateway api era que os dados json eram transmitidos em uma solicitação get. Sim, sim, esses objetos json muito adoráveis foram submetidos à codificação de URL e colocados na string de consulta. E tudo ficaria bem, quando de repente um dia ... a duração da solicitação de obtenção deixasse de ser suficiente. Estupidamente, o json codificado em url, kanalya, não se encaixava lá! O programador da “velha escola”, coçando a cabeça, perguntou:
“O que nós vamos fazer? Nosso json cresceu, mas não percebemos ... "
"Bem, talvez possamos publicá-las no correio?" Eu sugeri, então parece ser mais correto.
"Ok, passe para postar."
Era o número um.
2. Gerenciamento de tempo e backup
Para parafusar novas funcionalidades no projeto, foi necessário implementar
solicitações CRUD correspondentes no gateway, que é exatamente o que nosso camarada da “velha escola” realmente fez. O problema era que ele fazia isso uma vez a cada 3 dias, dando "Concluído, verifique". Às vezes, verificações mostravam que nem tudo funcionava, por exemplo, obter a lista é aceitável, adicionar um novo item não é ok. Demorou algum tempo para corrigir e refinar, após o qual foi possível liberar a funcionalidade no acesso em massa. A proposta de fazer a implementação de consultas no gateway por conta própria, porque é pelo menos mais rápida, foi rejeitada, porque "é difícil lá, você não vai entender". O resultado dessa abordagem foi o encerramento do trabalho "em mim". Se, por exemplo, fosse necessário consertar algo em massa no banco de dados, escolhendo entre 3 dias de espera e a implementação das correções por meio de consultas - escolhi a 2ª opção. Os clientes não gostam de esperar particularmente, o novo lançamento chegou de forma estável. Um desses introdutórios, ou seja, a fixação em massa de um sinal para os usuários de algum sinal, foi confiado a mim para implementar, havia uma hora para tudo, tudo, as autoridades estavam esperando por um relatório bonito. Aqui nos espera como número dois-s.
O fato é que o formato dos dados json transmitidos nas solicitações implicava apenas alguns campos obrigatórios, todos os outros eram arbitrários, uma estrutura clara e final não existia. Por exemplo, para adicionar um usuário, passei um json do formulário:
POST /api/users { "email":"ivanov@mail.ru", "password":"myEmailIsVeryBig", "name_last":"", "name_first":"", "name_middle":"", "birth":"01.01.1961", // , - "living_at":"., .3 .4 .24", "phone_num":"+70000000000" }
A parte opcional que foi transmitida nas solicitações de adição / atualização foi salva e fornecida na íntegra (falarei sobre como isso foi implementado abaixo). A linha inferior é, o tempo não pára, seria necessário resolver o problema - atualizar usuários, colocar suas etiquetas. Mas não dirija toda a estrutura toda vez? Deve verificar! Eu testei em mim mesmo - transmiti apenas um campo na solicitação de atualização, verifiquei, o campo apareceu e o restante dos dados está no local. A questão é fazer um loop pequeno e atualizar o restante.
O script bufou suavemente, recebendo e transmitindo dados, e tudo parecia estar indo bem ... quando de repente - uma ligação. "Não vemos o nome dos usuários no sistema!" - relatório desse lado do fio. “Vamos lá! Bem, deu certo! ” - Um calafrio desagradável correu pelas minhas costas. Investigações posteriores mostraram que, de fato, o nome "" foi indicado no nome, embora todos os outros dados estivessem no lugar. O que fazer em tal situação? Implante o backup!
Programador "camarada" da velha escola ", sem problemas! Precisa de backup! Quando é que o último relevante é feito? ” Eu pergunto.
"Uhh ... eu vou ver agora .... Não, não há bakapa. "
A situação foi salva pelo fato de que, algumas horas antes, finalizei e testei o módulo com relatórios, eu tinha uma caixa csv com todos os dados necessários, o pedido foi restaurado em mais uma hora.
A falta de documentação inteligível, uma descrição dos algoritmos de operação, verificações de validade de entrada e o mais importante - backups de bancos de dados - são o número dois-s.
Desde então, os backups começaram a ser removidos todos os dias.
3. Golpe profundo
Trêmulo, mas o trabalho estava em andamento, os problemas foram resolvidos, alguns mais rápidos, outros mais lentos, quando de repente ... os clientes perceberam que o sistema não era compreendido pelos servidores de outra pessoa e por essa atitude em relação ao PD e organização das atividades de ZI no ISPD eles não vão acariciar a cabeça. É necessário transferir o servidor para si mesmo.
Por que o sistema não foi originalmente transferido? A liderança tinha uma paixão - centralização. A gerência sonhava com um sistema que faria tudo! Você precisa anexar uma criança à escola? Você entra no sistema, em um escritório especial, onde envia uma inscrição. Você precisa, digamos, pedir pizza - você entra no sistema, em outro escritório especial, solicita pizza. Talvez você queira se comunicar com belas senhoras / senhores? Ao seu serviço, existe um terceiro gabinete especial - você também está enviando uma solicitação para lá e assim por diante ad infinitum.
Vantagens - um login e senha para tudo, os dados são armazenados com segurança no gateway. Existem até backups. E, lembre-se, ninguém vai tirar esse sistema de nós! E mesmo que isso leve embora - o que vem a seguir? Mesmo assim, eles não entenderão o sistema de proteção contra programadores da "velha escola" - tudo é complicado lá.
O VDS com o sistema foi descarregado, atribuído aos clientes, eles o implantaram, todo mundo dança e canta, beleza!
E então uma onda de curiosidade e alguma suspeita me cobriram.
Se nosso aplicativo da Web estiver cheio de falhas, onde estão os dados? Você realmente ficou em outros servidores? E se eles decidirem fechar o sistema de fora, tudo entrará em colapso?
Uma verificação simples mostrou que os dados, assim como os próprios processadores de gateway, estavam no mesmo servidor. E, não, eles não foram transferidos para lá por causa da transferência do servidor, eles estavam sempre lá.
Agora eu tinha a disposição o muito secreto desenvolvimento da "velha escola", que me propus a pesquisar. É claro que a engenharia reversa legal no estilo dos artigos da revista Hacker, com ollydbg, compensações e outras coisas legais, não funcionou, por isso estou compartilhando o que tenho.
O desenvolvimento em si foi implementado em python, havia apenas arquivos .pyc que podiam ser facilmente descompilados novamente em código legível. Francamente, demorou muito tempo, até 25 minutos, para descobrir como isso funciona.
Portanto, o complexo sistema desenvolvido pelo programador da “velha escola”, que poucos conseguem entender, consiste em:
- O script processado pelo Apache, que realmente recebeu a solicitação. O que esse script fez? Abriu uma conexão com uma porta localhost específica e passou uma solicitação para lá com todos os seus dados. Só isso. Os interesses vão além.
- A parte do servidor que processou solicitações do script. A lógica de suas ações foi bastante interessante. Primeiramente, não havia manipulação de dados no código e nenhuma consulta no banco de dados; em vez disso, as funções de banco de dados em PL \ SQL foram chamadas. Toda a lógica, verificações e assim por diante, tudo foi estabelecido na função de banco de dados. 50% do script era um dicionário que contém o nome da solicitação, a função associada a ela e os nomes dos parâmetros da função que devem corresponder aos dados transmitidos na linha de solicitação de obtenção. Dados JSON, se necessário, foram passados como um parâmetro separado. Um recurso da organização da parte do servidor era a conexão de backup durante a autenticação do usuário. Se o login e a senha foram encontrados no banco de dados, o ID da sessão foi gerado e a instância de conexão aberta foi dobrada no dicionário (e foi eliminada pelo tempo limite de 10 minutos para que não fosse eliminada - havia um método especial para prolongar a vida útil da sessão), a chave era o ID da sessão, diretamente no banco de dados não armazenado. Como exatamente o ID da sessão está associado aos dados do usuário? Afinal, há uma solicitação de dados nos quais o ID do usuário não é transmitido? Funciona, o que significa que algo está errado aqui.
Um desenvolvimento muito difícil foi dado à consciência com dificuldade e não teve pressa em revelar os segredos há muito perdidos dos mestres do passado.
Incrível (Vá para> Definição, obrigado PhpStorm por entender PL \ SQL), incompreensível para a mente do leigo que sofre o Conhecimento Verdadeiro da Civilização Perdida dos Programadores da Velha Escola. Em geral, quando conectado, uma tabela temporária foi gerada na função de verificação de dados de autenticação, na qual o ID do usuário foi armazenado.
Este foi apenas o começo, uma lista indicativa de vulnerabilidades sérias encontrada:
- DDoS usando autenticação em massa (as conexões eram reservadas e, portanto, repousavam no limite de conexão do DBMS, o que, dada a possibilidade de estender o tempo de vida da sessão, tornava possível encher completamente a memória com conexões, e o trabalho de novos usuários no sistema seria impossível);
- falta de proteção contra força bruta (o número de tentativas com falha de login não é detectado, não é armazenado, não é verificado;
- falta de controle sobre ações com entidades (por exemplo, a lista de documentos solicitados pelo usuário foi emitida levando em consideração a organização à qual o usuário está anexado e, se você souber o ID do documento, poderá executar com êxito a solicitação de atualização / exclusão do documento, e a lista de usuários é boa mesmo sem senhas que, aliás, eram armazenadas no banco de dados de forma clara, sem hash, podiam ser recebidas por qualquer pessoa).
E outro problema sério não é um esquema formal de armazenamento de dados. Como prometido anteriormente, estou falando sobre o armazenamento de "qualquer campo" do JSON. Não, eles não foram armazenados como uma linha na tabela. Eles foram divididos em pares de valores-chave e armazenados em uma tabela separada. Por exemplo, para usuários, havia duas tabelas - users e users_data (chave da string, valor da string) - onde os dados foram realmente armazenados. O resultado dessa abordagem foi um aumento no tempo com amostras complexas do banco de dados.
Na verdade, isso foi suficiente para tomar e implementar a decisão de transferir o sistema para uma nova API, compreensível, documentada e suportada.
Moral
Talvez este sistema seja "Legado", e o programador da "velha escola" que o criou seja a essência do Legado.
No entanto, as conclusões são as seguintes:
- Se lhe disserem que "lá é difícil, você não entenderá" - significa que há um atas completo
- Se eles são esmagados pela autoridade, então algo é impuro
- Confie, mas verifique - a segurança não é um estado, a segurança é um processo, além disso contínuo, portanto, é melhor garantir que as qualidades declaradas sejam verdadeiras do que descobrir mais tarde que todos os usuários se tornam "Ivanov Ivanov Ivanitch" de repente, mas não há bacaps.