Conceitos como memory-safety e type-safety existem desde pelo menos os anos 60, então o senso comum de que o assunto é algum tipo de "novidade" ou "moda" está errado. O senso comum de que isso é orientado por "mercado" também está errado, pois é puramente acadêmico. O senso comum de que "a linguagem não tem problemas de segurança, a culpa é do programador" também está errado. O senso comum de que segurança é uma dicotomia também está errado. E o último senso comum também está errado: segurança não é só sobre vulnerabilidades e código (implementação).
Quando digo que uma linguagem tem um problema (repare no termo usado) de segurança não estou dizendo que o código da implementação tem algo errado nem dizendo que a linguagem tem uma vulnerabilidade. Segurança é muito mais complexo do que isso, vulnerabilidade em código é só a ponta do iceberg.
Antes de explicar vale deixar uma nota: safe e secure não é a mesma coisa, os dois são traduzidos para "seguro" em português e acaba perdendo essa distinção. Mas safe é mais no sentido de confiável (como sua casa que é safe) e secure é mais no sentido de protegido (como um banco que é secure).
Unsafe
Deixa eu explicar o que é um código unsafe: é basicamente código onde resultados inesperados e fora do controle do programador podem acontecer. Um exemplo em C:
char *ex = malloc(32);
strcpy(ex, "hello");
Esse código é unsafe porque ex
pode ser NULL e quando strcpy()
recebe NULL o comportamento é indefinido (undefined behavior - UB). Ou seja, se o código pode causar UB então ele é chamado de unsafe ("não-confiável" traduzido de maneira não literal).
Linguagens que não tem UB no seu dialeto padrão são safe ("confiáveis") e a palavra-chave unsafe é usada justamente para indicar trechos de código que podem causar UB (código "não-confiável").
Ok, e o que UB tem a ver com segurança? Bem, eu explico: a gigantesca maioria das vulnerabilidades em binários são causadas por UB. Para servir de evidência do que estou falando vejamos as últimas CVE reportadas para o kernel Linux:
- CVE-2023-50431: sec_attest_info in drivers/accel/habanalabs/common/habanalabs_ioctl.c in the Linux kernel through 6.6.5 allows an information leak to user space because info->pad0 is not initialized.
- CVE-2023-47233: The brcm80211 component in the Linux kernel through 6.5.10 has a brcmf_cfg80211_detach use-after-free in the device unplugging (disconnect the USB by hotplug) code.
- CVE-2023-46862: An issue was discovered in the Linux kernel through 6.5.9. During a race with SQ thread exit, an io_uring/fdinfo.c io_uring_show_fdinfo NULL pointer dereference can occur.
- CVE-2023-46813: An issue was discovered in the Linux kernel before 6.5.9, exploitable by local users with userspace access to MMIO registers. Incorrect access checking in the #VC handler and instruction emulation of the SEV-ES emulation of MMIO accesses could lead to arbitrary write access to kernel memory (and thus privilege escalation). This depends on a race condition through which userspace can replace an instruction before the #VC handler reads it.
- CVE-2023-45898: The Linux kernel before 6.5.4 has an es1 use-after-free in fs/ext4/extents_status.c, related to ext4_es_insert_extent.
Ou seja, dentre as 5 últimas CVE do Linux todas elas são UB... Coincidência? Não.
Ok, agora você deve tá pensando: "ah, mas UB não é uma vulnerabilidade de segurança."
E ninguém disse que era, é um risco de segurança. Um risco e não uma vulnerabilidade. E riscos de segurança são problemas de segurança.
Pensa assim: você deixar seu carro aberto no meio da rua e com a chave na ignição não é um mal funcionamento na tranca do veículo nem na ignição. Mas mesmo assim você será roubado, porque você se sujeitou ao risco disso acontecer em um ambiente favorável a "dar merda".
O mesmo vale para riscos de segurança em linguagens de programação. Sim, podemos dizer que "a culpa é do programador" de certo modo: a culpa é dele por não ter conhecimento para entender o risco ao qual ele estava se sujeitando e fazer "pouco caso" do assunto.
E caso não tenha ficado claro, riscos de segurança são problemas de segurança. É um problema a ser resolvido, não é algo que deve ser aceito como "normal" ou como as coisas deveriam ser. Se o risco poderia ser evitado então isso é uma falha de design na linguagem de programação (pois é, problemas de segurança não existem apenas em código...).
Ok, riscos sempre existirão e não é tecnicamente possível mitigar todos. Mas existem vários riscos que podem ser mitigados, como os UB por exemplo.
Escolher por manter riscos que podem ser mitigados em linguagens de programação é como escolher deixar o carro aberto com a chave na ignição: é burrice, é óbvio que vai dar merda. Sempre deu e sempre dará.
Então não importa o quanto o programador tenha a crença de que é "foda", se o risco existe vai dar merda (lei de Murphy sempre funciona quando o assunto é segurança). A evidência disso é que todo projeto em C conhecido têm, todo ano, dezenas ou até centenas de CVE que foram causadas por causa de UB na linguagem.
Então o simples fato da linguagem não ter UB no seu dialeto padrão faz com que sim, a linguagem seja mais segura do que C (que tem muito UB).
Menos risco é sinônimo de mais segurança, e isso vale para segurança física também. Por exemplo: você está mais seguro em casa ou em uma corda bamba pendurada em um penhasco? O que é mais seguro: escalar um poste de alta tensão bêbado ou sóbrio e com todo o EPI necessário?
Repito: menos risco é mais segurança.