O mínimo sobre segurança que você precisa saber

Introdução

Em um cenário onde a segurança dos dados é fundamental, a gestão adequada das permissões de acesso em um banco de dados é essencial. Ao concedermos e revogarmos permissões de acesso aos objetos do banco, assumimos um controle preciso sobre quem pode realizar quais operações e onde. Esse controle é particularmente crucial em ambientes de produção, onde a integridade e a segurança dos dados são prioritárias.

Ao estabelecermos usuários com papéis bem definidos e acesso restrito, fortalecemos as defesas contra possíveis ameaças à segurança. Essa abordagem não apenas protege os dados sensíveis, mas também reduz o risco de acessos não autorizados e potenciais danos ao banco de dados.

Neste artigo, exploraremos a importância das permissões de acesso, papéis de usuários e como gerenciar essas permissões com os comandos GRANT e REVOKE.

É importante destacar que, para melhor entendimento da mudança de papéis ao longo dos exemplos, estamos usando o prompt do psql com uma personalização, com a seguinte configuração (dentro do arquivo psqlrc; mais informações no artigo Utilização do psql):

Debian shell: conteúdo do arquivo psqlrc
\set PROMPT1 '%n@%/%R%#%x '

Essa configuração exibe o nome do usuário na sessão atual, seguido de um “@”, e o nome da base de dados.

Roteiro:

  • GRANT / REVOKE:
    • Aplicação em SCHEMA;
    • Aplicação em TABLE e SEQUENCE;
    • Aplicação em COLUMN;
    • Aplicação em ROLE;
    • Aplicação em DATABASE;
    • Aplicação em FUNCTION / PROCEDURE.
  • ALTER DEFAULT PRIVILEGES
  • DROP OWNED / REASSIGN OWNED

GRANT / REVOKE

Os comandos GRANT e REVOKE são usados para conceder e revogar permissões de acesso a objetos de banco de dados, como tabelas, esquemas, funções e sequências. Eles desempenham um papel fundamental na gestão de segurança e controle de acesso no banco de dados.

O comando GRANT é usado para conceder permissões de acesso a usuários ou roles específicas em objetos de banco de dados. As permissões comumente concedidas incluem SELECT, INSERT, UPDATE, DELETE, USAGE, EXECUTE entre outras, e podem ser concedidas para objetos individuais ou para todos os objetos de um determinado tipo em um esquema. Por exemplo, você pode conceder a um usuário permissão para ler dados de uma tabela específica usando o seguinte comando:

Comandos SQL: concedendo um privilégio de tabela
 GRANT SELECT ON tabela TO usuário;

Por outro lado, o comando REVOKE é usado para revogar permissões previamente concedidas. Ele permite remover permissões de acesso de usuários ou roles em objetos de banco de dados específicos, restringindo acessos. Por exemplo, você pode revogar a permissão de um usuário para modificar dados em uma tabela usando o comando a seguir:

Comandos SQL: revogando um privilégio em uma tabela
 REVOKE UPDATE ON tabela FROM usuário;

Considerando nossas roles, schemas e usuários criados no artigo anterior, abaixo vamos exemplificar a aplicação de GRANT e REVOKE em diversos objetos para melhor entendimento, sempre utilizando o superusuário, que no nosso caso é o administrador do banco de dados (dba) para conceder e revogar privilégios.

Aplicação em SCHEMA

Aqui vamos conceder permissão de uso (USAGE) do schema aplicacao1 para os usuários app1_admin, app1_user1 e app1_user2, que possibilita consultar os objetos desse schema, por exemplo, as tabelas que criamos:

psql shell: concedendo privilégio USAGE em um schema
dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user1;

app1_user1@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
 id | nome  | idade
----+-------+-------
  1 | João  | 30
  2 | Maria | 25
  3 | Pedro | 40
(3 rows)

Antes de rodarmos o mesmo comando para o usuário app1_user2, confira o exemplo da tentativa de consulta da mesma tabela sem permissão no schema:

psql shell: tentando consultar dados sem privilégio em uma tabela
app1_user2@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
ERROR:  permission denied for schema aplicacao1
LINE 1: SELECT * FROM aplicacao1.tabela1_app1;

Como esse privilégio concedido diz especificamente sobre a possibilidade de consultar os objetos do esquema, não é possível modificá-los, como tentar inserir dados:

psql shell: tentanto inserir dados sem privilégio em uma tabela
app1_user1@aplicacao=>  INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES
('Novo nome', '0');
ERROR:  permission denied for table tabela1_app1

Isso será possível quando concedermos permissões a níveis de tabela, no próximo tópico.

Aplicação em TABLE e SEQUENCE

Agora, vamos conceder a permissão de consulta, inserção, atualização e exclusão de dados (SELECT, INSERT, UPDATE, DELETE, respectivamente) ao usuário app1_admin nas tabelas do schema aplicacao1 (tabela1_app1, tabela2_app1, tabela3_app1):

psql shell: concedendo privilégios de várias tabelas
dba@aplicacao=# GRANT SELECT, INSERT, UPDATE, DELETE ON aplicacao1.tabela1_app1 TO app1_admin;
GRANT SELECT, INSERT, UPDATE, DELETE ON aplicacao1.tabela2_app1 TO app1_admin;
GRANT SELECT, INSERT, UPDATE, DELETE ON aplicacao1.tabela3_app1 TO app1_admin;

Outra forma de fazer isso seria usando a cláusula ALL TABLES IN SCHEMA, o que facilita o processo encurtando o comando, portanto, aqui vamos conceder as permissões para o usuário app2_admin sobre as tabelas do schema aplicacao2 e a permissão de leitura para os demais usuários:

psql shell: concedendo privilégios da cláusula ALL TABLES IN SCHEMA
dba@aplicacao=# GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA aplicacao1 TO app1_admin;
dba@aplicacao=# GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA aplicacao2 TO app2_admin;
dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app1_user1;
dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app1_user2;
dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app2_user1;
dba@aplicacao=# GRANT SELECT ON ALL TABLES IN SCHEMA aplicacao2 TO app2_user2;

Além disso, para possibilitar que a inserção de dados ocorra bem, também precisamos conceder permissão de USAGE na sequência das tabelas, por exemplo, ‘tabela1_app1_id_seq’, por se tratar de uma tabela com uma coluna SERIAL (com o id de cada linha se autoincrementando). Para isso, vamos rodar:

psql shell: concedendo privilégio USAGE em sequências de uma tabela
dba@aplicacao=# GRANT USAGE ON SEQUENCE aplicacao1.tabela1_app1_id_seq TO app1_admin;

Sendo assim, o administrador da aplicação torna-se o único usuário que consegue inserir novas linhas nas tabelas selecionadas:

psql shell: inserindo dados em uma tabela
app1_admin@aplicacao=> INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES
(Novo nome', '0');

app1_admin@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
 id |    nome    | idade
----+------------+-------
  1 | João       | 30
  2 | Maria      | 25
  3 | Pedro      | 40
  4 | Novo nome  | 0
(4 rows)

Por consequência, caso o administrador da aplicação 2 (app2_admin) tente consultar ou realizar qualquer outra transação no schema da aplicação 1, haverá erro:

psql shell: tentativa de consulta sem privilégio em um schema
app2_admin@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
ERROR:  permission denied for schema aplicacao1
LINE 1: SELECT * FROM aplicacao1.tabela1_app1;

Em outro exemplo, temos uma tabela (aplicacao1.tabela3_app1) que estritamente não pode ser editada, e para esse controle iremos revogar a permissão de edição da role public, que engloba todos os usuários:

psql shell: revogando permissão de edição de todos os usuários em uma tabela
dba@aplicacao=# REVOKE DELETE, UPDATE ON aplicacao1.tabela3_app1 FROM public;
REVOKE

Assim, caso haja tentativa de edição nessa tabela por qualquer usuário além do DBA, ocorrerá o seguinte erro:

psql shell: tentativa de edição sem privilégio de uma tabela
app1_user1@aplicacao=> INSERT INTO aplicacao1.tabela3_app1 (email)
VALUES ('exemplo@example.com');
ERROR:  permission denied for table tabela3_app1
Time: 1.684 ms

Aplicação em COLUMN

Agora, vamos considerar uma situação em que o usuário app1_user1 precisa atualizar uma linha específica em uma das tabelas da aplicação 1, mas apenas em uma coluna em especial. Por exemplo, suponha que o usuário precise atualizar a idade de uma pessoa na tabela tabela1_app1.

Vamos conceder permissões apropriadas para que o usuário app1_user1 possa realizar essa operação:

psql shell: concedendo privilégio de edição de uma coluna
dba@aplicacao=# GRANT UPDATE (idade) ON aplicacao1.tabela1_app1 TO app1_user1;

Após conceder essa permissão, o usuário app1_user1 será capaz de atualizar a coluna idade na tabela tabela1_app1, mas não terá permissão para atualizar outras colunas ou realizar outras operações na tabela.

Por exemplo, o usuário app1_user1 pode executar a seguinte operação:

psql shell: editano dados de uma coluna específica
app1_user1@aplicacao=> UPDATE aplicacao1.tabela1_app1 SET idade = '35' WHERE id = 1;
app1_user1@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
 id |    nome    | idade
----+------------+-------
  2 | Maria      | 25
  3 | Pedro      | 40
  4 | Novo nome  | 0
  1 | João       | 35
(4 rows)

No entanto, se ele tentar atualizar outras colunas ou realizar outras operações, como inserção ou exclusão de linhas, ele receberá uma mensagem de erro informando que a permissão é negada para essa ação específica:

psql shell: tentativa de edição de uma tabela sem privilégio
app1_user1@aplicacao=> INSERT INTO aplicacao1.tabela1_app1 (nome, idade) VALUES ('Joana', '28');
ERROR:  permission denied for table tabela1_app1

E, após a modificação necessária, é possível remover essa permissão do usuário com o REVOKE:

psql shell: recvgando privilégio de edição da coluna
dba@aplicacao=# REVOKE UPDATE (idade) ON aplicacao1.tabela1_app1 FROM app1_user1;

No entanto, isso impossibilita novas alterações de idade por esse usuário:

psql shell: tentativa de edição da coluna sem privilégio
app1_user1@aplicacao=>  UPDATE aplicacao1.tabela1_app1 
SET idade = '30' 
WHERE id = 1;
ERROR:  permission denied for table tabela1_app1

Esse é um exemplo simples de como conceder permissões em nível de coluna para usuários específicos, permitindo que eles realizem operações precisas em dados específicos em uma tabela. Isso proporciona um controle granular sobre o que os usuários podem fazer em um banco de dados.

Aplicação em ROLE

Para exemplificar o controle de acesso utilizando role como um papel e não como um usuário, vamos criar uma nova role chamada editores_app1, que vai possibilitar que alguém se torne editor das tabelas do schema aplicacao1, tendo como administrador o usuário app1_admin; em seguida, adicionaremos o usuário app1_user2 nessa role.
Para criar a role:

psql shell: criando uma role com administrador
dba@aplicacao=# CREATE ROLE editores_app1 WITH ADMIN app1_admin;

Depois, vamos dar a permissão de edição nas tabelas para a role criada:

psql shell: concedendo privilégio em todas as tabelas do schema para role específica
dba@aplicacao=# GRANT UPDATE ON ALL TABLES IN SCHEMA aplicacao1 TO editores;

Agora, podemos adicionar o usuário app1_user2 nessa role:

psql shell: adicionando usuário em role
dba@aplicacao=# GRANT editores TO app1_user2;

Então, podemos editar o que for necessário com esse usuário (abaixo vemos uma consulta à tabela antes e pós-edição):

psql shell: consultando uma tabela , editando um dado e consultando novamente
app1_user2@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
 id |    nome    | idade
----+------------+-------
  2 | Maria      | 25
  3 | Pedro      | 40
  4 | Novo nome  | 0
  1 | João       | 35
(4 rows)

app1_user2@aplicacao=> UPDATE aplicacao1.tabela1_app1
SET nome = 'João Vitor'
WHERE id = 1;
UPDATE 1

app1_user2@aplicacao=> SELECT * FROM aplicacao1.tabela1_app1;
 id |    nome    | idade
----+------------+-------
  2 | Maria      | 25
  3 | Pedro      | 40
  4 | Novo nome  | 0
  1 | João Vitor | 35
(4 rows)

Após feita a alteração, também é possível remover esse usuário da role criada por meio do REVOKE:

psql shell: revogando de um usuário privilégios de uma role
dba@aplicacao=# REVOKE editores FROM app1_user2;
REVOKE ROLE

Como pudemos ver, utilizando roles, simplificamos o controle de acesso ao atribuir permissões específicas a papéis, gerenciando ambientes com vários usuários de maneira mais ampla e eficiente.

Aplicação em DATABASE

A concessão de permissões a nível de DATABASE oferece controle sobre todas as tabelas e objetos contidos dentro do banco de dados específico. Esse método é útil quando se deseja aplicar permissões de forma abrangente, abordando todas as tabelas e esquemas presentes no banco de dados.

Para exemplificar, consideremos o cenário em que precisamos revogar a permissão para conexão com os bancos de dados de um usuário. Vamos supor que o usuário app1_user1 não possa mais se conectar com nosso servidor por motivos de segurança. Para remover o acesso do usuário app1_user1, considerando a permissão de CONNECT, devemos começar revogando essa permissão da role public, que é uma role padrão do PostgreSQL, gerada automaticamente quando criamos um banco (também existe um schema padrão de mesmo nome, que todos os usuários podem mexer e acessar). Para isso, vamos rodar o seguinte comando:

psql shell: revogando privilégio de conexão de todos os usuários
dba@aplicacao=# REVOKE CONNECT ON DATABASE aplicacao FROM public;

Dessa forma, não é mais possível se conectar ao banco aplicacao. Para resolver isso, vamos conceder essa permissão separadamente para cada usuário que desejamos, desconsiderando, então, o usuário app1_user1:

psql shell: concedendo privilégio de conexão para vários usuários
dba@aplicacao=# GRANT CONNECT ON DATABASE aplicacao TO app1_admin, app1_user2, app2_admin, app2_user1, app2_user2;

Sendo assim, caso queiramos revogar o acesso de outro usuário, basta revogar a permissão CONNECT ao database aplicacao da role/usuário desejado. Essa é a visualização de erro na tentativa de acesso do usuário que não possui a permissão CONNECT:

psql shell: tentativa de conexão com o banco sem permissão
$ psql -h localhost -p 5432 -U app1_user1 -d aplicacao;
Password for user app1_user1:
psql: error: connection to server at "localhost" (::1), port 5432 failed: FATAL:  permission denied for database "aplicacao"
DETAIL:  User does not have CONNECT privilege.

Aplicação em FUNCTION/PROCEDURE

Vamos, agora, exemplificar o gerenciamento do acesso as functions concedendo permissão de execução para a função last_day, que pertence ao banco pagila (exemplo de banco de dados disponibilizado pelo PostgreSQL para fins educacionais e de treinamento). 

Para isso, o primeiro passo é revogar todas as permissões em relação às funções da role padrão public (aquele grupo em que todos os usuários estão), especificando o schema onde se encontra a função que desejamos gerenciar, no nosso caso, o schema pagila:

psql shell: revogando de todos os usuários o privilégio de todas as permissões de uma função
postgres@pagila=# REVOKE ALL ON FUNCTION schema_pagila.last_day FROM public;
REVOKE

Feito isso, assim como para acesso ao database, precisamos especificar quais usuários podem executar essa função explicitamente, já que removemos a permissão de todos ao revogar a role public. Então, para garantir que nosso usuário user_pagila possa executar essa função, podemos rodar:

psql shell: concedendo privilégio de execução de uma função para um usuário
postgres@pagila=# GRANT EXECUTE ON FUNCTION last_day TO user_pagila;
GRANT

Pronto! Para os usuários que não foram contemplados com a permissão, aparecerá a seguinte mensagem de erro:

psql shell: tentativa sem privilégio de execução de uma função
user_pagila2@pagila=> SELECT schema_pagila.last_day(current_date);
ERROR:  permission denied for function last_day

ALTER DEFAULT PRIVILEGES

O comando ALTER DEFAULT PRIVILEGES oferece uma maneira de configurar os privilégios padrão que serão aplicados a objetos criados no futuro. Em outras palavras, ele permite definir as permissões que novos objetos herdarão automaticamente assim que forem criados. 

Ao contrário do que muitos podem pensar, esse comando não afeta os privilégios de objetos existentes, apenas os novos objetos que serão criados após a configuração. Você pode definir privilégios padrão globalmente para todos os objetos criados no banco de dados atual ou especialmente para objetos criados em schemas específicos.

Por padrão, normalmente, os privilégios de novos objetos são herdados do papel de usuário que os cria. No entanto, o comando ALTER DEFAULT PRIVILEGES permite personalizar esses privilégios de acordo com as necessidades do ambiente. Por exemplo, você pode conceder permissões de leitura para o público em geral em todas as tabelas futuras criadas em um determinado esquema ou permitir que um papel específico execute funções recém-criadas.

Para exemplificar, vamos criar mais dois usuários, o app1_user3 e o app_user4:

psql shell: criando usuários
dba@aplicacao=# CREATE ROLE app1_user3 LOGIN PASSWORD 'senha';
dba@aplicacao=# CREATE ROLE app1_user4 LOGIN PASSWORD 'senha';

E, claro, conceder a eles o uso do schema aplicacao1, como também a permissão para se conectarem com a base de dados aplicacao:

psql shell: concedendo privilégio de uso de um schema para os dois novos usuários
dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user3;
dba@aplicacao=# GRANT USAGE ON SCHEMA aplicacao1 TO app1_user4;
psql shell: concedendo privilégio de conexão a um banco para os dois novos usuários
dba@aplicacao=# GRANT CONNECT ON DATABASE aplicacao TO app1_user3;
dba@aplicacao=# GRANT CONNECT ON DATABASE aplicacao TO app1_user4;

Então, vamos usar o comando ALTER DEFAULT PRIVILEGES para definir, como padrão, a possibilidade de consulta nas novas tabelas criadas para o usuário app1_user3:

psql shell: alterando padrão de privilégios com inclusão de consulta de tabelas no schema
dba@aplicacao=# ALTER DEFAULT PRIVILEGES IN SCHEMA aplicacao1 GRANT SELECT ON TABLES TO app1_user3;

Para testarmos, vamos criar uma nova tabela no schema aplicacao1, chamada tabela4_app1, inserindo, nela, dados de exemplo:

psql shell: criando uma nova tabela no schema e inserindo dados nela
dba@aplicacao=# CREATE TABLE aplicacao1.tabela4_app1 (
    id SERIAL PRIMARY KEY,
    dado VARCHAR(150)
);
CREATE TABLE

dba@aplicacao=# INSERT INTO aplicacao1.tabela4_app1 (dado)
VALUES ('algum dado');
INSERT 0 1

Assim, é possível que o usuário app1_user3 consulte as informações da tabela por mais que o comando de GRANT SELECT não tenha sido rodado explicitamente; isso graças ao ALTER DEFAULT PRIVILEGES rodado antes da criação da tabela, que também servirá para futuros objetos caso criemos mais:

psql shell: consultando dados da nova tabela por um usuário com privilégio fornecido pelo ALTER DEFAULT PRIVILEGES
app1_user3@aplicacao=> SELECT * FROM aplicacao1.tabela4_app1;
 id |    dado
----+------------
  1 | algum dado
(1 row)

Em contrapartida, o outro usuário, o app1_user4, não pode fazer a consulta porque não rodamos o ALTER DEFAULT PRIVILEGES para ele e nem o GRANT SELECT de forma explícita para essa tabela:

psql shell: tentativa de consulta de dados de uma tabela sem privilégios para isso
app1_user4@aplicacao=> SELECT * FROM aplicacao1.tabela5_app1;
ERROR:  permission denied for table tabela5_app1

DROP OWNED / REASSIGN OWNED

Os comandos DROP OWNED e REASSIGN OWNED são usados para gerenciar a propriedade dos objetos do banco de dados, sendo úteis em situações de manutenção de usuários. Enquanto o DROP OWNED remove todos os objetos pertencentes a uma role específica e revoga suas permissões, o REASSIGN OWNED transfere a propriedade desses objetos para outra role, sem removê-los.

DROP OWNED

O comando DROP OWNED é utilizado para remover todos os objetos (como tabelas, sequências, funções etc.) que pertencem a uma ou mais roles específicas, além de revogar todas as permissões que essas roles possuem em outros objetos do banco de dados. É frequentemente usado antes de excluir um usuário ou uma role, garantindo que não restem objetos associados ou permissões residuais.

Por exemplo, suponha que você precise remover todos os objetos pertencentes à role app1_admin. Para isso, você pode usar o seguinte comando:

Comandos SQL: removendo objetos pertencentes a um usuário
DROP OWNED BY app1_admin CASCADE;

Isso removerá todos os objetos que pertencem à app1_admin e revogará todas as permissões associadas, incluindo a remoção de qualquer objeto que dependa dos que pertencem a essa role.

Outra opção desse comando é a RESTRICT, que impede a remoção de objetos caso existam dependências, ou seja, outros objetos que dependam daqueles que pertencem à role especificada. Essa opção é útil quando você deseja garantir que não está removendo inadvertidamente objetos importantes que poderiam afetar o funcionamento do banco de dados. Veja um exemplo de erro de dependência:

psql shell: erro na remoção de objetos por dependência
ERROR:  cannot drop objects owned by role because other objects depend on them
DETAIL:  Table "tabela_dependente" depends on table "tabela1_app1".

Esse erro pode ser resolvido com a RESTRICT:

Comandos SQL: removendo objetos com restrição daqueles com dependência
DROP OWNED BY app1_user2 RESTRICT;

REASSIGN OWNED

O comando REASSIGN OWNED permite transferir a propriedade de todos os objetos pertencentes a uma role para outra, servindo para quando um usuário ou uma role estão prestes a serem removidos, mas seus objetos associados ainda precisam ser mantidos, só que sob a propriedade de outra role. 

Por exemplo, para transferir todos os objetos da app1_admin para dba, podemos utilizar o seguinte comando:

Comandos SQL: transferindo propriedade de objetos de um usuário para outro
REASSIGN OWNED BY app1_admin TO dba;

Com esse comando, todos os objetos que pertenciam à role app1_admin agora passam a ser de propriedade da role dba, garantindo a continuidade de acesso e administração.

Conclusão

Lidar com segurança é chato; não tem adjetivo melhor que esse para descrever o trabalho de dar permissões de tabela em tabela para cada usuário. Ninguém gosta, nem os mais perfeccionistas. No entanto, em tempos de bancos de dados na nuvem, isso é importantíssimo. Colocamos este capítulo logo no começo para que você se acostume a lidar com esse tipo de coisa desde já. Acredite, dói menos assim. 

O PostgreSQL tem um jeitinho aqui e ali de facilitar as coisas, mas se você não começar a pensar em segurança logo no início, depois vai ficar bem mais difícil e, sem dúvida, mais tedioso. Pense nisso agora. E pense nisso em todos os ambientes de trabalho, seja durante o desenvolvimento, seja em testes, homologação etc. Se deixar para ver isso só quando chegar no ambiente de produção, com certeza você terá problemas e receberá comentários desagradáveis quando sofrer uma auditoria de segurança ou, pior, quando sofrer uma tentativa de invasão no seu banco de dados. Ninguém quer que isso aconteça, claro, mas a questão não é se isso um dia vai acontecer, mas quando.

Compartilhe

Você pode gostar

pg_hba.conf

Introdução O arquivo pg_hba.conf (PostgreSQL Host-Based Authentication) é uma peça fundamental na configuração de segurança de qualquer instância PostgreSQL. Ele define as regras de autenticação

Tuning de SO (no Linux)

Introdução Tuning refere-se ao processo de ajustar e otimizar o desempenho de um sistema, software ou aplicação. A otimização do sistema operacional é uma etapa

plugins premium WordPress